diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 975107d..0000000 --- a/.babelrc +++ /dev/null @@ -1,20 +0,0 @@ -{ - "plugins": [ - "babel-plugin-transform-typescript-metadata", - [ - "@babel/plugin-proposal-decorators", - { - "legacy": true - } - ], - [ - "@babel/plugin-proposal-class-properties", - { - "loose": true - } - ] - ], - "presets": [ - "@babel/preset-typescript" - ] -} \ No newline at end of file diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 0000000..b93b3b3 --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,6 @@ +reviews: + auto_review: + enabled: true + # Use ".*" to match all branches and review every PR regardless of target + base_branches: + - ".*" diff --git a/.cursorignore b/.cursorignore index 022991c..5ac45fa 100644 --- a/.cursorignore +++ b/.cursorignore @@ -6,18 +6,15 @@ dist/ coverage/ tmp/ out-tsc/ +test-output/ # Tooling caches -.nx/cache/ -.nx/workspace-data/ +.nx/ +.pnpm-store/ .cache/ -.sass-cache/ -typings/ # Logs and runtime artifacts *.log -connect.lock -libpeerconnection.log # Environment files .env @@ -26,17 +23,21 @@ libpeerconnection.log !supabase/.env.example # Supabase local state +supabase/generated-types.ts supabase/.temp/ supabase/.branches/ supabase/.env +supabase/.env.keys +supabase/.env.local +supabase/.env.*.local +legacy/supabase/.temp/ +legacy/supabase/.branches/ + +# AI agent local state +.claude/worktrees/ # OS / editor noise .DS_Store Thumbs.db .idea/ -.c9/ -.settings/ -.project -.classpath -*.launch *.sublime-workspace diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f2d79f4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,43 @@ +# Dependencies and build output — reinstalled/rebuilt inside the image. +node_modules/ +dist/ +tmp/ +out-tsc/ +coverage/ +test-output/ + +# Nx local cache +.nx/ + +# Legacy reference tree — excluded from the graph; not needed to build api/worker. +legacy/ + +# VCS and local env +.git/ +.gitignore +.env +.env.* + +# Supabase local state (types are committed; CLI state is not) +supabase/.temp/ +supabase/.branches/ +supabase/.env +supabase/.env.* +supabase/generated-types.ts + +# IDE, tooling caches, OS +.vscode/ +.idea/ +.cache/ +.DS_Store +Thumbs.db +*.log + +# Docs and plans — not needed at build time +docs/ +.cursor/ +.claude/ + +# Docker context meta +Dockerfile +.dockerignore diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..d9b8099 --- /dev/null +++ b/.env.example @@ -0,0 +1,24 @@ +# Shared workspace env for all apps +# Copy to `.env` at the repo root. +# +# Nx loads env files when you run targets (`nx serve api`, etc.): +# 1) apps//.env (host-specific — wins on conflicts) +# 2) this root .env (shared defaults) +# Production injects env via the platform (no .env files in the image). +# Secrets are validated per provider via requireEnv (ADR-007) — not dotenv-safe. +# +# Fill after `pnpm supabase start`. Local CLI may still label keys as +# anon / service_role in `supabase status`; our env names follow Supabase’s +# publishable / secret terminology (legacy JWT keys still work as values). + +# --- Required (all apps) — admin / secret client -------------------------------- +SUPABASE_URL="http://127.0.0.1:54321" +SUPABASE_SECRET_KEY= + +# --- Logging (optional; used when nestjs-pino is restored) ------------------------- +# LOG_LEVEL=info +# LOG_PRETTY=true + +# --- Config artifact (optional; ADR-007) ------------------------------------------ +# NXT_CONFIG_PATH= +# NXT_CONFIG_JSON= diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..88f9924 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +supabase/** @bobbybol diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..51d3e86 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,64 @@ +name: CI + +on: + pull_request: + branches: [oss-migration] + push: + branches: [oss-migration] + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + ## + # Core CI - lint, test, typecheck affected files + ## + nx-affected-checks: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + - uses: actions/setup-node@v6 + with: + node-version: '24' + - run: corepack enable + - run: pnpm install --frozen-lockfile + - uses: nrwl/nx-set-shas@v5 + with: + main-branch-name: oss-migration + - run: pnpm exec nx affected -t lint test typecheck build --parallel=3 + + ## + # Schema type-drift guard — disabled during the oss-migration phase. + # Re-enable (uncomment both jobs below) once migration lands on main. + ## + # supabase-type-filter: + # runs-on: ubuntu-latest + # outputs: + # schema: ${{ steps.filter.outputs.schema }} + # steps: + # - uses: actions/checkout@v6 + # - uses: dorny/paths-filter@v4 + # id: filter + # with: + # filters: | + # schema: + # - 'supabase/**' + # - 'libs/core/src/types/**' + + # supabase-type-drift: + # needs: supabase-type-filter + # if: needs.supabase-type-filter.outputs.schema == 'true' + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v6 + # - uses: actions/setup-node@v6 + # with: + # node-version: '24' + # - run: corepack enable + # - run: pnpm install --frozen-lockfile + # - run: pnpm supabase db start + # - run: pnpm gen-types-local && pnpm gen-better-types + # - run: git diff --exit-code libs/core/src/types/supabase-types.ts diff --git a/.github/workflows/deploy-to-do.yml b/.github/workflows/deploy-to-do.yml deleted file mode 100644 index 61921ec..0000000 --- a/.github/workflows/deploy-to-do.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Deploy to DigitalOcean App Platform - -on: - pull_request: - types: [opened, synchronize] - -jobs: - deploy-to-do: - name: Deploy App to DigitalOcean - runs-on: ubuntu-latest - - steps: - # Checkout the code - - name: Checkout code - uses: actions/checkout@v3 - - # Wait for Supabase Branch Setup to Complete - # - name: Wait for Supabase Branch Setup - # id: wait_supabase - # run: | - # echo "Checking Supabase branch status..." - # for i in {1..20}; do - # STATUS=$(curl -s -H "Authorization: Bearer ${{ secrets.SUPABASE_API_KEY }}" \ - # "https://api.supabase.io/projects/YOUR_PROJECT_ID/branches" \ - # | jq -r '.branches[] | select(.name == "pr-${{ github.event.number }}") | .status') - - # echo "Current status: $STATUS" - # if [[ "$STATUS" == "ready" ]]; then - # echo "Supabase branch is ready." - # exit 0 - # fi - # echo "Waiting for branch setup to complete..." - # sleep 30 - # done - # echo "Supabase branch setup timed out." - # exit 1 - - # # Deploy App to DigitalOcean - # - name: Deploy to DigitalOcean - # id: deploy_app - # run: | - # echo "Deploying new app to DigitalOcean..." - # APP_SPEC=$(jq -n \ - # --arg appName "pr-${{ github.event.number }}-app" \ - # --arg branchUrl "https://${{ github.event.number }}-branch.supabase.co" \ - # '{ - # name: $appName, - # region: "nyc3", # Change to your preferred region - # services: [ - # { - # name: "web", - # github: { - # repo: "your-github-org-or-user/your-repo-name", - # branch: "${{ github.head_ref }}", - # deploy_on_push: false - # }, - # envs: [ - # { key: "SUPABASE_URL", value: $branchUrl }, - # { key: "SUPABASE_ANON_KEY", value: "your-anon-key" } - # ] - # } - # ] - # }') - - # echo "$APP_SPEC" > app_spec.json - - # curl -X POST \ - # -H "Authorization: Bearer ${{ secrets.DIGITALOCEAN_API_KEY }}" \ - # -H "Content-Type: application/json" \ - # -d @app_spec.json \ - # "https://api.digitalocean.com/v2/apps" diff --git a/.gitignore b/.gitignore index a7fd725..32986bb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,52 +1,58 @@ -# See http://help.github.com/ignore-files/ for more about ignoring files. - -# compiled output -dist -tmp -/out-tsc - -# dependencies -node_modules -# temporary until we fix dependencies -package-lock.json - -# IDEs and editors -/.idea -.project -.classpath -.c9/ -*.launch -.settings/ -*.sublime-workspace +# See https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files + +# Build / compile output (any project depth) +dist/ +tmp/ +out-tsc/ + +# Dependencies +node_modules/ + +# Nx +.nx/ + +# Test output +test-output +coverage/ + +# Environment & secrets +.env +.env.* +!**/.env.example + +# Supabase local state (root chain + legacy reference) +supabase/generated-types.ts +supabase/.temp/ +supabase/.branches/ +supabase/.env +supabase/.env.keys +supabase/.env.local +supabase/.env.*.local +legacy/supabase/.temp/ +legacy/supabase/.branches/ + +# Logs & runtime artifacts +*.log -# IDE - VSCode +# Tooling caches +.pnpm-store/ +.cache/ + +# IDE — VSCode (track shared workspace settings) .vscode/* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json -# misc -/.sass-cache -/connect.lock -/coverage -/libpeerconnection.log -npm-debug.log -yarn-error.log -testem.log -/typings - -# System Files -.DS_Store -Thumbs.db - -.env -_NOTES +# IDE — other editors +.idea/ +*.sublime-workspace -.nx/cache -# Supabase -supabase/.temp -supabase/.branches -supabase/.env +# AI agent local state +.claude/worktrees/ +.claude/settings.local.json -.nx/ +# OS +.DS_Store +Thumbs.db diff --git a/.husky/pre-commit b/.husky/pre-commit old mode 100755 new mode 100644 index d6b7578..3057fd5 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,5 +1,2 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - -npx lint-staged && npx tsc -p tsconfig.base.json --noEmit --allowSyntheticDefaultImports -# && npx nx run-many --parallel --target=test +lint-staged +pnpm exec nx affected -t typecheck --uncommitted diff --git a/.nvmrc b/.nvmrc index f483565..54c6511 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v22.9.0 \ No newline at end of file +v24 diff --git a/.nxignore b/.nxignore index 5a19e8a..68e379e 100644 --- a/.nxignore +++ b/.nxignore @@ -1,3 +1,9 @@ -node_modules -dist -coverage \ No newline at end of file +# Frozen reference tree — not part of the active Nx workspace (see legacy/README.md) +legacy + +# Non-runtime files — @nx/js:node watches the whole project when build is +# nx:run-commands; ignore these so `nx serve` does not restart on docs / httpYac edits. +docs +**/*.http +**/*.md +apps/*/http diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 4220fba..eabe9f9 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -2,6 +2,7 @@ "recommendations": [ "dbaeumer.vscode-eslint", "tombonnike.vscode-status-bar-format-toggle", - "firsttris.vscode-jest-runner" + "firsttris.vscode-jest-runner", + "anweber.vscode-httpyac" ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index a8d6b17..7ff2552 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,68 +1,55 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", - "name": "Tiamat", - "runtimeExecutable": "nx", + "name": "Debug api with Nx", + "runtimeExecutable": "pnpm", "runtimeArgs": [ + "exec", + "nx", "serve", - "tiamat" + "api" ], + "env": { + "NODE_OPTIONS": "--inspect=9229" + }, "console": "integratedTerminal", - "outFiles": [ - "${workspaceFolder}/**/*.js", - "!**/node_modules/**" - ] - }, - { - "type": "node", - "request": "launch", - "name": "Talos", - "runtimeExecutable": "nx", - "runtimeArgs": [ - "serve", - "talos", + "internalConsoleOptions": "neverOpen", + "skipFiles": [ + "/**" ], - "console": "integratedTerminal", + "sourceMaps": true, "outFiles": [ - "${workspaceFolder}/**/*.js", + "${workspaceFolder}/apps/api/dist/**/*.(m|c|)js", "!**/node_modules/**" ] }, { "type": "node", "request": "launch", - "name": "Yeti", - "runtimeExecutable": "nx", + "name": "Debug worker with Nx", + "runtimeExecutable": "pnpm", "runtimeArgs": [ + "exec", + "nx", "serve", - "yeti", + "worker" ], + "env": { + "NODE_OPTIONS": "--inspect=9230" + }, "console": "integratedTerminal", - "outFiles": [ - "${workspaceFolder}/**/*.js", - "!**/node_modules/**" - ] - }, - { - "type": "node", - "request": "launch", - "name": "Loch", - "runtimeExecutable": "nx", - "runtimeArgs": [ - "serve", - "loch", + "internalConsoleOptions": "neverOpen", + "skipFiles": [ + "/**" ], - "console": "integratedTerminal", + "sourceMaps": true, "outFiles": [ - "${workspaceFolder}/**/*.js", + "${workspaceFolder}/apps/worker/dist/**/*.(m|c|)js", "!**/node_modules/**" - ], - }, + ] + } ] -} \ No newline at end of file +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 51124a1..35c20d8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,7 @@ "editor.tabSize": 2, "search.exclude": { "package-lock.json": true, + "pnpm-lock.yaml": true, "libs/*/migration": true, "supabase/migrations": true }, @@ -13,5 +14,6 @@ "editor.defaultFormatter": "dbaeumer.vscode-eslint" }, "deno.enable": false, - "js/ts.tsdk.path": "node_modules/typescript/lib" + "js/ts.tsdk.path": "node_modules/typescript/lib", + "jest.runMode": "on-demand" } diff --git a/AGENTS.md b/AGENTS.md index debc702..bad6189 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,17 +7,28 @@ You are a senior TypeScript programmer with experience in the NestJS framework a - Always create a plan before acting, and list actions in to-dos if more than one - Always address the to-dos one by one, stopping between points to await code review and acceptance - Never jump to the next point of the to-dos until prompted to do so +- The maintainer creates git commits. Do not commit, amend, or undo commits unless the maintainer explicitly asks you to do so for that specific action. +- **No unsanctioned exploration.** When a command fails, do not launch open-ended debugging (long shell chains, repeated Docker/CI runs, simulated environments, scratch scripts, or “let me investigate” loops). Stay on repo files and short, task-specific commands. Propose a minimal fix or ask the maintainer; only run deeper investigation if they explicitly ask you to. ## Communication - **Ask questions inline in the chat.** Write questions as normal messages in your response. - **Never use structured question-picker / multiple-choice UI** (e.g. Cursor's AskQuestion tool). If you need a decision, ask in plain text and wait for the human's reply so they can add nuance, extra context, or instructions. +- Keep answers concise and direct. ## Commands +pnpm + Nx 23. Active projects: `api`, `worker`, `core`. -- **Type checking:** `npm run check-types` -- **Linting:** `npm run eslint` -- **Both (lint + type check):** `npm run lint` +- **Lint bar:** `pnpm exec nx run-many -t lint typecheck build test -p api,worker,core` +- **Lint:** `pnpm exec nx run-many -t lint -p api,worker,core` +- **Typecheck:** `pnpm exec nx run-many -t typecheck -p api,worker,core` +- **One project:** `pnpm exec nx run :lint` (or `:typecheck`, `:build`, `:test`) +- **Affected (CI):** `pnpm exec nx affected -t lint test typecheck build --parallel=3` + +Pre-commit: ESLint on staged `.ts`, then `nx affected -t typecheck --uncommitted`. + +- **Serve:** `pnpm exec nx serve api` / `worker` +- **Type-gen:** `pnpm generate-types:local` (needs `pnpm supabase start`) ## Backend (this repo) @@ -82,6 +93,10 @@ changes, or work clearly outside the domains below. | Configuration & wiring | 007 | | Open-source migration strategy | 008 | | Database migration deployment & governance | 009 | +| Meter command batches, load shedding, meter grouping | 011 | +| Company cutover strategy | 012 | +| Capability vs core boundaries, behavior/module placement | 013 | +| Machine credentials — API keys, scopes, Postgres roles, MCP | 014 | ### How to read (progressive) diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4c2784a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,52 @@ +# syntax=docker/dockerfile:1 +# Parameterized multi-stage image for @nxt/api and @nxt/worker (ADR-006 decision 7). +# Build: docker build --build-arg APP=api . +# docker build --build-arg APP=worker . +# +# Runtime pruning uses Nx prune targets (not pnpm deploy) — see 002c decisions log [6]. + +ARG APP=api + +# ── base ────────────────────────────────────────────────────────────────────── +FROM node:24-slim AS base +ENV PNPM_HOME="/pnpm" +ENV PATH="${PNPM_HOME}:${PATH}" +RUN corepack enable +WORKDIR /workspace + +# ── build ───────────────────────────────────────────────────────────────────── +FROM base AS build +ARG APP + +# Manifests first so install layer caches when only source changes. +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +COPY apps/api/package.json apps/api/ +COPY apps/worker/package.json apps/worker/ +COPY libs/core/package.json libs/core/ + +RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store \ + pnpm install --frozen-lockfile + +COPY . . + +# Build + pruned package.json / lockfile / workspace_modules in apps/${APP}/dist. +RUN pnpm exec nx sync +RUN pnpm exec nx run "${APP}:prune" + +# ── runtime ─────────────────────────────────────────────────────────────────── +FROM base AS runtime +ARG APP +ENV NODE_ENV=production +ENV CI=true +WORKDIR /app + +# Webpack + Nx prune output: main.js, config.default.json, pruned manifests, workspace_modules/. +COPY --from=build --chown=node:node /workspace/apps/${APP}/dist ./ + +# Hoisted linker so workspace_modules/@nxt/core can resolve its own deps (e.g. zod). +RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store \ + pnpm install --prod --frozen-lockfile --config.node-linker=hoisted + +USER node +EXPOSE 3000 +CMD ["node", "main.js"] diff --git a/README.md b/README.md index 7b21c96..ab08681 100644 --- a/README.md +++ b/README.md @@ -1,271 +1,30 @@ -# NXT backend +# NXT Backend -**NXT backend** is an open-source backend platform for managing off-grid and mini-grid electricity systems. It handles payment processing, remote monitoring of production and distribution infrastructure, and remote interaction with smart prepaid electricity meters. +NestJS / Nx monorepo for a self-hostable mini-grid operations platform (Supabase Auth + Postgres, +optional Timescale for time-series). -The platform is designed for organizations that operate mini-grids in emerging markets — enabling them to manage customers, meters, grids, payouts, and real-time energy data from a single system. +## Status ---- +**Foundation only — not feature-complete.** `main` currently ships the always-on Foundation from the +OSS migration (auth, API keys, accounts/members/organizations, user-admin, grids entity/seed, `api` + +`worker` hosts, canonical Supabase migrations). Capability domains (production monitoring, metering, +payments, notifications, field ops, automation) are **still being imported**; do not treat this tip +as a full replacement for the legacy multi-app stack. -## Table of Contents +- Migration roadmap: [`docs/plans/002-oss-migration.md`](docs/plans/002-oss-migration.md) +- Architecture ADRs: [`docs/architecture/`](docs/architecture/) +- Agent / maintainer commands: [`AGENTS.md`](AGENTS.md) +- Pre-migration tree (frozen reference): [`legacy/`](legacy/) -- [Architecture](#architecture) -- [Apps](#apps) -- [Libraries](#libraries) -- [Tech Stack](#tech-stack) -- [Prerequisites](#prerequisites) -- [Getting Started](#getting-started) -- [Environment Variables](#environment-variables) -- [Supabase](#supabase) -- [Building and Running Apps](#building-and-running-apps) -- [Type Checking and Linting](#type-checking-and-linting) -- [Known Issues](#known-issues) -- [License](#license) +## Commands ---- - -## Architecture - -NXT backend is structured as an **Nx monorepo** containing multiple NestJS backend applications and shared libraries. Each application has a distinct responsibility: - -``` -nxt-backend/ -├── apps/ -│ ├── tiamat/ # Main API — operations, payments, meters, users -│ ├── talos/ # Meter provisioning and hardware registration -│ ├── loch/ # Background jobs, automation, and integrations -│ └── yeti/ # Time-series data collection and grid monitoring -├── libs/ -│ ├── core/ # Shared NestJS modules (auth, alerts, etc.) -│ ├── helpers/ # Shared utility functions -│ └── timeseries/ # TimescaleDB integration layer -└── supabase/ # Database migrations, functions, and seed data -``` - -Authentication is handled via **Supabase Auth**. The primary database is **Supabase (PostgreSQL)**. Time-series data (snapshots, energy readings, forecasts) is stored in a separate **TimescaleDB** instance. - ---- - -## Apps - -### `tiamat` — Main Operations API - -The primary REST + WebSocket API consumed by the frontend dashboards. Covers: - -- **Meter management** — remote meter interactions, device messages, install sessions, directive batches -- **Grid infrastructure** — grids, distribution control units (DCUs), MPPTs -- **Payments** — orders, wallets, payouts, Flutterwave integration -- **Users & organizations** — multi-tenancy, user admin, roles -- **Notifications** — email (SendGrid), SMS (Africa's Talking), Telegram, USSD sessions -- **Field data collection** — EpiCollect integration -- **LoRaWAN** — ChirpStack network server integration -- **Automation** — autopilot, directive watchdog -- **Reporting** — data analytics, CSV/XLSX downloads - -### `talos` — Meter Provisioning Service - -Handles meter hardware registration and provisioning workflows. Currently supports **CALIN** meters (v1 and v2 API). Designed to accommodate additional meter brands in the future. - -### `loch` — Background Jobs & Integrations - -Long-running background service responsible for: - -- **MQTT monitoring** — subscribes to Victron Energy MQTT brokers for live battery/solar state -- **Automated jobs** — lifeline credit top-ups, revenue updates, autopilot triggers -- **Notification dispatch** — outbound email, SMS, Telegram -- **Third-party integrations** — Make.com automation webhooks, weather data, sunrise/sunset scheduling -- **EpiCollect** — inbound field survey data processing - -### `yeti` — Time-Series Data Collector - -Periodic data collection and aggregation service writing to TimescaleDB: - -- **Snapshots** — meter, grid, MPPT, router, DCU at intervals from 1 minute to 1 day -- **Forecasting** — solar irradiance via Solcast integration -- **Digital twin** — live grid state representation -- **Grid diagnostics** — health and connectivity monitoring -- **Exchange rates** — daily currency rate snapshots - ---- - -## Libraries - -### `core` -Shared NestJS modules used across apps: authentication guards, software dev alerting, and other cross-cutting concerns. - -### `helpers` -Shared utility functions and TypeScript helpers. - -### `timeseries` -Integration layer for TimescaleDB — connection management and shared query utilities. - ---- - -## Tech Stack - -| Layer | Technology | -|---|---| -| Framework | [NestJS](https://nestjs.com/) | -| Monorepo tooling | [Nx](https://nx.dev/) | -| Language | TypeScript | -| Primary database | [Supabase](https://supabase.com/) (PostgreSQL) | -| Time-series database | [TimescaleDB](https://www.timescale.com/) via [TypeORM](https://typeorm.io/) | -| Authentication | Supabase Auth | -| Real-time | WebSockets (Socket.IO) | -| Messaging | MQTT | -| Email | SendGrid | -| SMS | Africa's Talking | -| Payments | Flutterwave | -| Solar forecasting | Solcast | -| LoRaWAN | ChirpStack | -| Meter hardware | CALIN (v1 + v2 API) | - -> **Note on TypeORM:** You will find TypeORM used in two distinct contexts in this codebase. -> The integration with **TimescaleDB** uses TypeORM intentionally and permanently — it is the chosen ORM for that database and will remain so. -> The integration with the **primary Supabase (PostgreSQL) database**, however, is legacy: the project originally used TypeORM there and has been gradually migrating to the Supabase client. That migration is incomplete, so some modules still use TypeORM entities and repositories against the main database. This is known technical debt and contributions that continue the migration are welcome. - ---- - -## Prerequisites - -- [Node.js](https://nodejs.org/) (check `.nvmrc` or `package.json` for the required version) -- [Docker](https://www.docker.com/) (required for running Supabase locally) -- [Supabase CLI](https://supabase.com/docs/guides/cli) -- [Nx CLI](https://nx.dev/getting-started/intro) (optional — can use `npx nx`) - ---- - -## Getting Started - -```bash -# Install dependencies -npm install - -# Start Supabase locally (requires Docker) -npx supabase start - -# Copy and fill in environment variables -cp apps/tiamat/.env.example apps/tiamat/.env -cp apps/talos/.env.example apps/talos/.env -cp apps/loch/.env.example apps/loch/.env -cp apps/yeti/.env.example apps/yeti/.env - -# Run the main API -npx nx serve tiamat +```sh +pnpm install +pnpm exec supabase start # local stack +pnpm exec supabase db reset # migrations + seed +pnpm generate-types:local # after schema changes +pnpm exec nx serve api # or: worker +pnpm exec nx run-many -t lint typecheck build test -p api,worker,core ``` ---- - -## Environment Variables - -Each app has a `.env.example` file listing the required environment variables with descriptions. Copy each one to `.env` and fill in the values for your environment before running. - -All secrets (API keys, database credentials, passwords) must be provided via environment variables — no credentials are hardcoded in the codebase. - ---- - -## Supabase - -### Start a local Supabase session -```bash -npx supabase start -``` - -### Pull the latest database schema from production -```bash -npx supabase db pull -``` - -### Reset the local database -```bash -npx supabase db reset -``` - -### Generate TypeScript types - -From a local Supabase instance: -```bash -npm run generate-types:local -``` - -From a remote/production Supabase project: -```bash -npx supabase login # if not already logged in -npm run generate-types -``` - -> **Note:** The generated `Json` type is automatically post-processed to `Record` instead of the default recursive union type, allowing TypeScript to properly access properties on JSON fields. The script `.scripts/fix-json-type.js` handles this automatically during type generation. - ---- - -## Building and Running Apps - -### Build an app -```bash -npx nx build - -# Examples: -npx nx build tiamat -npx nx build talos -npx nx build loch -npx nx build yeti - -# Build with production configuration -npx nx build --configuration=production -``` - -### Run an app (development) -```bash -npx nx serve - -# Examples: -npx nx serve tiamat -npx nx serve talos -npx nx serve loch -npx nx serve yeti - -# Run with production configuration -npx nx serve --configuration=production -``` - ---- - -## Type Checking and Linting - -```bash -# Type checking only -npm run check-types - -# Linting only -npm run eslint - -# Both -npm run lint -``` - ---- - -## Known Issues - -Older packages are causing high CPU usage in some environments. If the Nx process keeps running in the background after stopping a serve command: - -```bash -# Find the process -ps aux | grep nx - -# Kill it -kill -9 -``` - -Alternatively, check Activity Monitor (macOS) or Task Manager (Windows) for a lingering `nx` process. - ---- - -## License - -NXT backend is licensed under the [Mozilla Public License 2.0](https://www.mozilla.org/MPL/2.0/). See [LICENSE](./LICENSE) for the full text. - ---- - -## Authors - -NXT backend was created by **Bobby Bol** and **Tommaso Girotto**. See [AUTHORS.md](./AUTHORS.md) for details. +See `AGENTS.md` for the full command set and workflow. diff --git a/apps/api/.env.example b/apps/api/.env.example new file mode 100644 index 0000000..3970982 --- /dev/null +++ b/apps/api/.env.example @@ -0,0 +1,21 @@ +# Host-specific env for `api`. +# Copy to `apps/api/.env`. +# +# Shared secrets (`SUPABASE_URL`, `SUPABASE_SECRET_KEY`, LOG_*, NXT_CONFIG_*) live in +# the repo-root `.env` — see root `.env.example`. Nx merges: this file first, then root +# (first definition wins). + +# --- Optional host port ----------------------------------------------------------- +# PORT=3000 + +# --- Auth (api only — AuthModule / Passport strategies) ----------------------------- +# Required when serving api. Validated via requireEnv in SupabaseStrategy (not worker). +# Publishable key (replaces legacy anon key) — user-scoped / RLS-exercising clients. +SUPABASE_PUBLISHABLE_KEY= +# +# Preferred JWT verification (asymmetric / JWKS). Local CLI: +SUPABASE_JWKS_URL="http://127.0.0.1:54321/auth/v1/.well-known/jwks.json" +# +# Legacy HS256 — used only when SUPABASE_JWKS_URL is unset. +# Both may be set; JWKS takes precedence (no runtime fallback if JWKS is unreachable). +# SUPABASE_JWT_SECRET= diff --git a/apps/api/.spec.swcrc b/apps/api/.spec.swcrc new file mode 100644 index 0000000..3b52a53 --- /dev/null +++ b/apps/api/.spec.swcrc @@ -0,0 +1,22 @@ +{ + "jsc": { + "target": "es2017", + "parser": { + "syntax": "typescript", + "decorators": true, + "dynamicImport": true + }, + "transform": { + "decoratorMetadata": true, + "legacyDecorator": true + }, + "keepClassNames": true, + "externalHelpers": true, + "loose": true + }, + "module": { + "type": "es6" + }, + "sourceMaps": true, + "exclude": [] +} diff --git a/apps/api/eslint.config.mjs b/apps/api/eslint.config.mjs new file mode 100644 index 0000000..724052a --- /dev/null +++ b/apps/api/eslint.config.mjs @@ -0,0 +1,5 @@ +import baseConfig from "../../eslint.config.mjs"; + +export default [ + ...baseConfig +]; diff --git a/apps/api/http/.httpyac.js b/apps/api/http/.httpyac.js new file mode 100644 index 0000000..07be10a --- /dev/null +++ b/apps/api/http/.httpyac.js @@ -0,0 +1,18 @@ +/** + * httpYac environments — switch with the httpYac env picker in the status bar. + * Secrets come from parent `.env` files (`apps/api/.env` / repo root), same as `nx serve api`. + */ +module.exports = { + environments: { + $shared: { + baseUrl: 'http://localhost:3000', + supabaseUrl: 'http://127.0.0.1:54321', + platformEmail: 'superadmin@nxt-platform.com', + platformPassword: 'superadmin', + solarEmail: 'admin@nxt-solar.com', + solarPassword: 'admin', + devApiKey: 'dev-api-key-platform-superadmin', + }, + local: {}, + }, +}; diff --git a/apps/api/http/README.md b/apps/api/http/README.md new file mode 100644 index 0000000..83831b5 --- /dev/null +++ b/apps/api/http/README.md @@ -0,0 +1,53 @@ +# Local API smoke tests (httpYac) + +File-based requests for the [httpYac](https://marketplace.visualstudio.com/items?itemName=anweber.vscode-httpyac) extension — `.http` files live in the repo next to the API. + +## Setup + +1. Install **httpYac** VS Code extension (`anweber.vscode-httpyac`) and disable any other http extension you might have. +2. Use the same env as `nx serve api` — httpYac loads `.env` from parent folders + (`apps/api/.env` / repo root). Needs at least `SUPABASE_PUBLISHABLE_KEY` for login. +3. In the editor status bar, select httpYac environment **`local`** (URLs + seeded users from + `.httpyac.js`). +4. Local Supabase + seed, and `pnpm exec nx serve api`. + +## How to run + +Open an endpoint file (e.g. `me.http`) and **Send** a request. +`# @import ./login.http` + `# @ref loginPlatform` runs the login first (cached until you +force-refresh), then the API call uses `{{loginPlatform.access_token}}`. + +## Files + +| File | Role | +|------|------| +| `.httpyac.js` | Shared local URLs + seeded emails/passwords | +| `login.http` | Named Supabase password grants (`loginPlatform`, `loginSolar`) | +| `me.http` | Imports login, refs it, calls `GET /auth/me` | +| `user-admin.http` | Task 9: create/update/delete customer, agent, member (+ API-key create-customer) | + +## Seeded users + +| Persona | Email | Password | Login name | +|---------|-------|----------|------------| +| Platform | `superadmin@nxt-platform.com` | `superadmin` | `loginPlatform` | +| Solar | `admin@nxt-solar.com` | `admin` | `loginSolar` | + +New endpoint file pattern: + +```http +# @import ./login.http + +### Some endpoint +# @ref loginPlatform +GET {{baseUrl}}/… +Authorization: Bearer {{loginPlatform.access_token}} +``` + +## `user-admin.http` notes + +- Seed already covers this: solar org **2**, grid **1**, platform API key. No seed change needed. +- Run requests **top-down** when a step needs a prior `# @name` id (create → update → delete). +- Creates real Auth users; timestamps keep emails unique. If Auth/phone collides, reset: + `pnpm exec supabase db reset`. +- Machine smoke: **Create customer (X-API-KEY)** — privileged admin path (no user client yet). diff --git a/apps/api/http/login.http b/apps/api/http/login.http new file mode 100644 index 0000000..11957e1 --- /dev/null +++ b/apps/api/http/login.http @@ -0,0 +1,26 @@ +# Shared Supabase password-grant logins (seeded users). +# Imported by endpoint files via `# @import ./login.http` + `# @ref …` + +@publishableKey = {{$dotenv SUPABASE_PUBLISHABLE_KEY}} + +### Login — platform superadmin +# @name loginPlatform +POST {{supabaseUrl}}/auth/v1/token?grant_type=password +apikey: {{publishableKey}} +Content-Type: application/json + +{ + "email": "{{platformEmail}}", + "password": "{{platformPassword}}" +} + +### Login — solar developer admin +# @name loginSolar +POST {{supabaseUrl}}/auth/v1/token?grant_type=password +apikey: {{publishableKey}} +Content-Type: application/json + +{ + "email": "{{solarEmail}}", + "password": "{{solarPassword}}" +} diff --git a/apps/api/http/me.http b/apps/api/http/me.http new file mode 100644 index 0000000..3dd45d0 --- /dev/null +++ b/apps/api/http/me.http @@ -0,0 +1,15 @@ +# @import ./login.http + +### GET /auth/me (platform) +# @ref loginPlatform +GET {{baseUrl}}/auth/me +Authorization: Bearer {{loginPlatform.access_token}} + +### GET /auth/me (solar) +# @ref loginSolar +GET {{baseUrl}}/auth/me +Authorization: Bearer {{loginSolar.access_token}} + +### GET /auth/me (X-API-KEY — seeded platform key) +GET {{baseUrl}}/auth/me +X-API-KEY: {{devApiKey}} diff --git a/apps/api/http/user-admin.http b/apps/api/http/user-admin.http new file mode 100644 index 0000000..e5f96d8 --- /dev/null +++ b/apps/api/http/user-admin.http @@ -0,0 +1,118 @@ +# @import ./login.http +# +# Task 9 smoke — privileged user-admin (whole-method admin client). +# Needs: local Supabase + seed, `nx serve api`, httpYac env `local`. +# Creates Auth users + rows — use unique emails/phones (below) or `supabase db reset` +# between full runs if something collides. + +### Create customer (solar bearer → grid 1) +# @name createCustomerSolar +# @ref loginSolar +POST {{baseUrl}}/user-admin/create-customer +Authorization: Bearer {{loginSolar.access_token}} +Content-Type: application/json + +{ + "full_name": "Smoke Customer", + "email": "smoke-customer-{{$timestamp}}@example.com", + "grid_id": 1, + "is_hidden_from_reporting": false +} + +### Update customer (solar bearer) +# @ref loginSolar +POST {{baseUrl}}/user-admin/update-customer +Authorization: Bearer {{loginSolar.access_token}} +Content-Type: application/json + +{ + "id": {{createCustomerSolar.id}}, + "full_name": "Smoke Customer Updated", + "email": "{{createCustomerSolar.account.email}}", + "is_hidden_from_reporting": true +} + +### Create customer (X-API-KEY — machine path, platform key) +# @name createCustomerApiKey +POST {{baseUrl}}/user-admin/create-customer +X-API-KEY: {{devApiKey}} +Content-Type: application/json + +{ + "full_name": "Smoke Machine Customer", + "email": "smoke-machine-customer-{{$timestamp}}@example.com", + "grid_id": 1, + "is_hidden_from_reporting": false +} + +### Create agent (solar bearer → grid 1) +# @name createAgentSolar +# @ref loginSolar +POST {{baseUrl}}/user-admin/create-agent +Authorization: Bearer {{loginSolar.access_token}} +Content-Type: application/json + +{ + "full_name": "Smoke Agent", + "phone": "+316{{$randomInt 10000000 99999999}}", + "email": "smoke-agent-{{$timestamp}}@example.com", + "grid_id": 1 +} + +### Update agent (solar bearer) +# @ref loginSolar +POST {{baseUrl}}/user-admin/update-agent +Authorization: Bearer {{loginSolar.access_token}} +Content-Type: application/json + +{ + "id": {{createAgentSolar.id}}, + "full_name": "Smoke Agent Updated", + "phone": "+316{{$randomInt 10000000 99999999}}", + "email": "smoke-agent-updated-{{$timestamp}}@example.com" +} + +### Invite member (solar bearer → org 2) +# @name inviteMemberSolar +# @ref loginSolar +POST {{baseUrl}}/user-admin/invite-member +Authorization: Bearer {{loginSolar.access_token}} +Content-Type: application/json + +{ + "email": "smoke-member-{{$timestamp}}@example.com", + "full_name": "Smoke Member", + "organization_id": 2, + "member_type": "TECH", + "redirectTo": "http://localhost:5173/" +} + +### Update member (solar bearer — invited member) +# @ref loginSolar +POST {{baseUrl}}/user-admin/update-member +Authorization: Bearer {{loginSolar.access_token}} +Content-Type: application/json + +{ + "id": {{inviteMemberSolar.id}}, + "full_name": "Smoke Member Updated", + "member_type": "TECH", + "training_level": 1, + "subscribed_to_telegram_revenue_notifications": false, + "hidden": false +} + +### Delete customer created by solar bearer (cleanup) +# @ref loginSolar +DELETE {{baseUrl}}/user-admin/customer/{{createCustomerSolar.id}} +Authorization: Bearer {{loginSolar.access_token}} + +### Delete agent (cleanup) +# @ref loginSolar +DELETE {{baseUrl}}/user-admin/agent/{{createAgentSolar.id}} +Authorization: Bearer {{loginSolar.access_token}} + +### Delete invited member (cleanup — member id from invite response) +# @ref loginSolar +DELETE {{baseUrl}}/user-admin/member/{{inviteMemberSolar.id}} +Authorization: Bearer {{loginSolar.access_token}} diff --git a/apps/api/jest.config.cts b/apps/api/jest.config.cts new file mode 100644 index 0000000..4591a17 --- /dev/null +++ b/apps/api/jest.config.cts @@ -0,0 +1,9 @@ +const { shared } = require('./jest.shared.cjs'); + +/** Default `nx test api` — unit only (no local Supabase / stack). */ +module.exports = { + ...shared, + displayName: 'api', + testMatch: [ '/test/unit/**/*.(spec|test).ts' ], + passWithNoTests: true, +}; diff --git a/apps/api/jest.e2e.config.cts b/apps/api/jest.e2e.config.cts new file mode 100644 index 0000000..2c84471 --- /dev/null +++ b/apps/api/jest.e2e.config.cts @@ -0,0 +1,9 @@ +const { shared } = require('./jest.shared.cjs'); + +/** `nx run api:test-e2e` — needs local Supabase + seed. */ +module.exports = { + ...shared, + displayName: 'api-e2e', + testMatch: [ '/test/e2e/**/*.(spec|test).ts' ], + passWithNoTests: true, +}; diff --git a/apps/api/jest.integration.config.cts b/apps/api/jest.integration.config.cts new file mode 100644 index 0000000..1bed660 --- /dev/null +++ b/apps/api/jest.integration.config.cts @@ -0,0 +1,9 @@ +const { shared } = require('./jest.shared.cjs'); + +/** `nx run api:test-integration` — needs local Supabase + seed. */ +module.exports = { + ...shared, + displayName: 'api-integration', + testMatch: [ '/test/integration/**/*.(spec|test).ts' ], + passWithNoTests: true, +}; diff --git a/apps/api/jest.shared.cjs b/apps/api/jest.shared.cjs new file mode 100644 index 0000000..f4649cd --- /dev/null +++ b/apps/api/jest.shared.cjs @@ -0,0 +1,24 @@ +/* eslint-disable */ +const { readFileSync } = require('fs') + +const swcJestConfig = JSON.parse( + readFileSync(`${__dirname}/.spec.swcrc`, 'utf-8') +); +swcJestConfig.swcrc = false; + +/** Shared Jest options for api unit / integration / e2e configs. */ +const shared = { + preset: '../../jest.preset.js', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': ['@swc/jest', swcJestConfig] + }, + moduleFileExtensions: ['ts', 'js', 'html'], + // Source uses explicit `.js` extensions on relative imports (NodeNext); strip for Jest. + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1', + }, + coverageDirectory: 'test-output/jest/coverage' +}; + +module.exports = { shared }; diff --git a/apps/api/package.json b/apps/api/package.json new file mode 100644 index 0000000..f75b577 --- /dev/null +++ b/apps/api/package.json @@ -0,0 +1,138 @@ +{ + "name": "@nxt/api", + "version": "0.0.1", + "license": "MPL-2.0", + "authors": [ + { + "name": "Bobby Bol", + "email": "studio.innua@gmail.com" + }, + { + "name": "Tommaso Girotto", + "email": "tommaso.girotto91@gmail.com" + } + ], + "private": true, + "nx": { + "name": "api", + "targets": { + "build": { + "executor": "nx:run-commands", + "options": { + "command": "webpack-cli build", + "env": { + "NODE_ENV": "production" + }, + "cwd": "apps/api" + }, + "configurations": { + "development": { + "env": { + "NODE_ENV": "development" + } + } + } + }, + "prune-lockfile": { + "dependsOn": [ + "build" + ], + "cache": true, + "executor": "@nx/js:prune-lockfile", + "outputs": [ + "{workspaceRoot}/apps/api/dist/package.json", + "{workspaceRoot}/apps/api/dist/pnpm-lock.yaml" + ], + "options": { + "buildTarget": "build" + } + }, + "copy-workspace-modules": { + "dependsOn": [ + "build" + ], + "cache": true, + "outputs": [ + "{workspaceRoot}/apps/api/dist/workspace_modules" + ], + "executor": "@nx/js:copy-workspace-modules", + "options": { + "buildTarget": "build" + } + }, + "prune": { + "dependsOn": [ + "prune-lockfile", + "copy-workspace-modules" + ], + "executor": "nx:noop" + }, + "serve": { + "continuous": true, + "executor": "@nx/js:node", + "defaultConfiguration": "development", + "dependsOn": [ + "build" + ], + "options": { + "buildTarget": "api:build", + "runBuildTargetDependencies": false + }, + "configurations": { + "development": { + "buildTarget": "api:build:development" + }, + "production": { + "buildTarget": "api:build:production" + } + } + }, + "test-integration": { + "executor": "nx:run-commands", + "dependsOn": [ + "^build" + ], + "options": { + "cwd": "apps/api", + "command": "jest --config jest.integration.config.cts" + } + }, + "test-e2e": { + "executor": "nx:run-commands", + "dependsOn": [ + "^build" + ], + "options": { + "cwd": "apps/api", + "command": "jest --config jest.e2e.config.cts" + } + } + } + }, + "dependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/core": "^11.0.0", + "@nestjs/passport": "^11.0.5", + "@nestjs/platform-express": "^11.0.0", + "@nxt/core": "workspace:*", + "@supabase/supabase-js": "^2.110.5", + "class-transformer": "^0.5.1", + "class-validator": "^0.15.1", + "jose": "^6.2.3", + "passport": "^0.7.0", + "passport-headerapikey": "^1.2.2", + "passport-http-bearer": "^1.0.1", + "ramda": "^0.31.3", + "reflect-metadata": "^0.2.0", + "rxjs": "^7.8.0", + "tslib": "^2.3.0" + }, + "devDependencies": { + "@nestjs/testing": "^11.0.0", + "@types/passport": "^1.0.17", + "@types/passport-http-bearer": "^1.0.42", + "@types/ramda": "^0.31.1", + "@types/supertest": "^7.2.1", + "supertest": "^7.2.2" + } +} diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts new file mode 100644 index 0000000..989fa0a --- /dev/null +++ b/apps/api/src/main.ts @@ -0,0 +1,28 @@ +import { NestFactory } from '@nestjs/core'; +import { Logger, ValidationPipe } from '@nestjs/common'; +import { loadConfig } from '@nxt/core/config'; + +async function bootstrap() { + // Config subpath has no Nest module side effects. loadConfig before NestFactory.create + // so forRootAsync factories / providers can call getConfig() (ADR-007 decision 3). + loadConfig(); + const { AppModule } = await import('./modules/app.module.js'); + + const app = await NestFactory.create(AppModule); + const logger = new Logger('Bootstrap'); + + app.enableCors(); + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + transformOptions: { enableImplicitConversion: false }, + }), + ); + + const port = process.env.PORT || 3000; + await app.listen(port); + logger.log(`Application is running on: http://localhost:${ port }`); +} + +bootstrap(); diff --git a/apps/api/src/modules/app.module.ts b/apps/api/src/modules/app.module.ts new file mode 100644 index 0000000..58010a0 --- /dev/null +++ b/apps/api/src/modules/app.module.ts @@ -0,0 +1,22 @@ +import { Module } from '@nestjs/common'; +import { + GlobalHttpModule, + GlobalLoggerModule, + GlobalSupabaseModule, +} from '@nxt/core'; +import { AuthModule } from './auth/auth.module.js'; +import { HealthModule } from './health/health.module.js'; +import { UserAdminModule } from './user-admin/user-admin.module.js'; + +/** Cross-cutting infra — Logger, Supabase, HTTP. */ +const infrastructure = [ GlobalLoggerModule, GlobalSupabaseModule, GlobalHttpModule ]; + +/** Always-on Foundation domain for this host. */ +const foundation = [ AuthModule, HealthModule, UserAdminModule ]; + +// Tier-1 capability conditionals (empty until capabilities are imported). + +@Module({ + imports: [ ...infrastructure, ...foundation ], +}) +export class AppModule {} diff --git a/apps/api/src/modules/auth/api-key.strategy.ts b/apps/api/src/modules/auth/api-key.strategy.ts new file mode 100644 index 0000000..04cc495 --- /dev/null +++ b/apps/api/src/modules/auth/api-key.strategy.ts @@ -0,0 +1,66 @@ +import { Injectable, UnauthorizedException } from '@nestjs/common'; +import { PassportStrategy } from '@nestjs/passport'; +import { HeaderAPIKeyStrategy } from 'passport-headerapikey'; +import { SupabaseService } from '@nxt/core'; + +import type { AuthenticatedUser } from './authenticated-user.js'; + +@Injectable() +export class ApiKeyStrategy extends PassportStrategy(HeaderAPIKeyStrategy) { + constructor(private readonly supabase: SupabaseService) { + super({ header: 'X-API-KEY', prefix: '' }, false); + } + + async validate(apiKey: string): Promise { + const apiKeyWithDetails = await this.supabase.adminClient + .from('api_keys') + .select(` + id, + account:accounts ( + id, + email, + full_name, + deleted_at, + supabase_id, + member:members ( + member_type + ), + organization:organizations ( + id + ) + ) + `) + .eq('key', apiKey) + .maybeSingle() + .then(this.supabase.handleResponse) + ; + + const account = apiKeyWithDetails?.account; + if (!account || account.deleted_at) { + throw new UnauthorizedException( + 'The API key used does not have a corresponding account', + ); + } + + const { member, organization } = account; + if (!member?.member_type || organization?.id == null) { + throw new UnauthorizedException( + 'The API key account is missing member or organization claims', + ); + } + + // Near-future (ADR-014 §5.3): attach an `authenticated` user client with this + // account's JWT claims so machine callers can exercise RLS. Until then, handlers + // that need a DB client after API-key auth fall through to service_role (high privilege). + return { + email: account.email ?? '', + full_name: account.full_name ?? '', + account_type: 'MEMBER', + member_type: member.member_type, + account_id: account.id, + organization_id: organization.id, + supabase_id: account.supabase_id, + async validate() { return {}; }, + }; + } +} diff --git a/apps/api/src/modules/auth/auth.controller.ts b/apps/api/src/modules/auth/auth.controller.ts new file mode 100644 index 0000000..bcd922f --- /dev/null +++ b/apps/api/src/modules/auth/auth.controller.ts @@ -0,0 +1,25 @@ +import { Controller, Get, UseGuards } from '@nestjs/common'; + +import { + CurrentUser, + type AuthenticatedUser, +} from './authenticated-user.js'; +import { AuthenticationGuard } from './authentication.guard.js'; + +/** Early probe for JWKS / API-key auth — returns the authenticated principal. */ +@UseGuards(AuthenticationGuard) +@Controller('auth') +export class AuthController { + @Get('me') + me(@CurrentUser() user: AuthenticatedUser) { + return { + email: user.email, + full_name: user.full_name, + account_type: user.account_type, + member_type: user.member_type, + account_id: user.account_id, + organization_id: user.organization_id, + supabase_id: user.supabase_id, + }; + } +} diff --git a/apps/api/src/modules/auth/auth.module.ts b/apps/api/src/modules/auth/auth.module.ts new file mode 100644 index 0000000..906c2ad --- /dev/null +++ b/apps/api/src/modules/auth/auth.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common'; +import { PassportModule } from '@nestjs/passport'; + +import { ApiKeyStrategy } from './api-key.strategy.js'; +import { AuthController } from './auth.controller.js'; +import { AuthenticationGuard } from './authentication.guard.js'; +import { SupabaseStrategy } from './supabase.strategy.js'; + +/** Host auth — Passport strategies + guard (admin client used for API-key lookup). */ +@Module({ + imports: [ PassportModule ], + controllers: [ AuthController ], + providers: [ AuthenticationGuard, SupabaseStrategy, ApiKeyStrategy ], + exports: [ AuthenticationGuard, PassportModule ], +}) +export class AuthModule {} diff --git a/apps/api/src/modules/auth/authenticated-user.ts b/apps/api/src/modules/auth/authenticated-user.ts new file mode 100644 index 0000000..8963779 --- /dev/null +++ b/apps/api/src/modules/auth/authenticated-user.ts @@ -0,0 +1,45 @@ +import { createParamDecorator, type ExecutionContext } from '@nestjs/common'; +import type { PostgrestError, SupabaseClient, User } from '@supabase/supabase-js'; +import type { + AccountTypeEnum, + MemberTypeEnum, +} from '@nxt/core/types/supabase-types'; +import type { Database } from '@nxt/core/types/supabase-types-adjusted'; + +/** + * Principal attached by Passport after bearer or API-key validation. + * No embedded account row (legacy TEMPORARY attach dropped); use `account_id` / claims. + */ +export interface AuthenticatedUser { + email: string; + full_name: string; + account_type: AccountTypeEnum; + member_type: MemberTypeEnum; + account_id: number; + organization_id: number; + supabase_id: string; + /** Per-request user client (RLS). Absent on API-key auth until a user client is wired. */ + supabase?: { + client: SupabaseClient; + handleResponse({ + data, + error, + status, + }: { + data: T; + error: PostgrestError | null; + status: number; + }): T; + }; + /** + * Re-checks the session with Supabase Auth when a user client is present. + * API-key path may stub this until a privileged equivalent exists. + */ + validate(args?: { organization_id?: number }): Promise>; +} + +export const CurrentUser = createParamDecorator( + (_data: unknown, context: ExecutionContext): AuthenticatedUser => { + return context.switchToHttp().getRequest<{ user: AuthenticatedUser }>().user; + }, +); diff --git a/apps/api/src/modules/auth/authentication.guard.ts b/apps/api/src/modules/auth/authentication.guard.ts new file mode 100644 index 0000000..57fbec8 --- /dev/null +++ b/apps/api/src/modules/auth/authentication.guard.ts @@ -0,0 +1,9 @@ +import { Injectable } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; + +/** + * Accepts either a Supabase bearer JWT (`supabase`) or an `X-API-KEY` header + * (`headerapikey`). + */ +@Injectable() +export class AuthenticationGuard extends AuthGuard([ 'supabase', 'headerapikey' ]) {} diff --git a/apps/api/src/modules/auth/supabase.strategy.ts b/apps/api/src/modules/auth/supabase.strategy.ts new file mode 100644 index 0000000..0c8bb6f --- /dev/null +++ b/apps/api/src/modules/auth/supabase.strategy.ts @@ -0,0 +1,152 @@ +import { + Injectable, + Logger, + UnauthorizedException, +} from '@nestjs/common'; +import { PassportStrategy } from '@nestjs/passport'; +import { createClient, type PostgrestError } from '@supabase/supabase-js'; +import { createRemoteJWKSet, jwtVerify, type JWTPayload } from 'jose'; +import { Strategy } from 'passport-http-bearer'; +import { requireEnv } from '@nxt/core/config'; +import { throwSupabaseError } from '@nxt/core'; +import type { + AccountTypeEnum, + MemberTypeEnum, +} from '@nxt/core/types/supabase-types'; +import type { Database } from '@nxt/core/types/supabase-types-adjusted'; + +import type { AuthenticatedUser } from './authenticated-user.js'; + +interface AppMetadataClaims { + account_type?: AccountTypeEnum; + member_type?: MemberTypeEnum; + account_id?: number | string; + organization_id?: number | string; +} + +interface UserMetadataClaims { + full_name?: string; +} + +@Injectable() +export class SupabaseStrategy extends PassportStrategy(Strategy, 'supabase') { + private readonly logger = new Logger(SupabaseStrategy.name); + private readonly supabaseUrl: string; + private readonly publishableKey: string; + private readonly jwks: ReturnType | null; + private readonly jwtSecret: Uint8Array | null; + + constructor() { + super(); + this.supabaseUrl = requireEnv('SUPABASE_URL'); + this.publishableKey = requireEnv('SUPABASE_PUBLISHABLE_KEY'); + + // @TODO :: This supports the legacy JWT SECRET :: Remove when all clients upgraded + const jwksUrl = process.env.SUPABASE_JWKS_URL; + const jwtSecret = process.env.SUPABASE_JWT_SECRET; + if (jwksUrl) { + this.jwks = createRemoteJWKSet(new URL(jwksUrl)); + this.jwtSecret = null; + } + else if (jwtSecret) { + this.jwks = null; + this.jwtSecret = new TextEncoder().encode(jwtSecret); + } + else { + throw new Error('MISSING SUPABASE_JWKS_URL or SUPABASE_JWT_SECRET'); + } + } + + async validate(token: string): Promise { + let payload: JWTPayload; + try { + if (this.jwks) { + payload = (await jwtVerify(token, this.jwks)).payload; + } + else if (this.jwtSecret) { + payload = (await jwtVerify(token, this.jwtSecret, { algorithms: [ 'HS256' ] })).payload; + } + else { + throw new Error('JWT verification is not configured'); + } + } + catch (err) { + if (err instanceof UnauthorizedException) { + throw err; + } + // Expected for bad/expired client tokens — keep the log quiet (no stack dump). + const reason = err instanceof Error ? err.message : String(err); + this.logger.warn(`Rejecting bearer token: ${ reason }`); + throw new UnauthorizedException('Invalid or expired token'); + } + + if (!payload.sub) { + throw new UnauthorizedException('No UID in token'); + } + + const email = typeof payload.email === 'string' ? payload.email : ''; + const userMetadata = (payload.user_metadata ?? {}) as UserMetadataClaims; + const appMetadata = (payload.app_metadata ?? {}) as AppMetadataClaims; + + const account_id = Number(appMetadata.account_id); + if (!Number.isFinite(account_id) || account_id <= 0) { + throw new UnauthorizedException('This auth.user does not have a corresponding account'); + } + + const organization_id = Number(appMetadata.organization_id); + if (!Number.isFinite(organization_id) || organization_id <= 0) { + throw new UnauthorizedException('Token is missing organization_id'); + } + + if (!appMetadata.account_type || !appMetadata.member_type) { + throw new UnauthorizedException('Token is missing account_type or member_type'); + } + + const supabaseClient = createClient( + this.supabaseUrl, + this.publishableKey, + { global: { headers: { Authorization: `Bearer ${ token }` } } }, + ); + + const logger = this.logger; + return { + email, + full_name: userMetadata.full_name ?? '', + account_type: appMetadata.account_type, + member_type: appMetadata.member_type, + account_id, + organization_id, + supabase_id: payload.sub, + supabase: { + client: supabaseClient, + handleResponse({ + data, + error, + status, + }: { + data: T; + error: PostgrestError | null; + status: number; + }): T { + if (error) { + throwSupabaseError(error, status, logger); + } + return data; + }, + }, + validate({ organization_id: _organizationId }: { organization_id?: number } = {}) { + return supabaseClient.auth + .getUser() + .then(({ data, error }) => { + if (error) { + throwSupabaseError(error, 401, logger); + } + if (!data?.user) { + throw new UnauthorizedException('No valid user data found'); + } + return data.user; + }); + }, + }; + } +} diff --git a/apps/api/src/modules/health/health.controller.ts b/apps/api/src/modules/health/health.controller.ts new file mode 100644 index 0000000..367b3d4 --- /dev/null +++ b/apps/api/src/modules/health/health.controller.ts @@ -0,0 +1,13 @@ +import { Controller, Get } from '@nestjs/common'; + +import { HealthService } from './health.service'; + +@Controller() +export class HealthController { + constructor(private readonly healthService: HealthService) {} + + @Get('health') + getHealth() { + return this.healthService.getHealth(); + } +} diff --git a/apps/api/src/modules/health/health.module.ts b/apps/api/src/modules/health/health.module.ts new file mode 100644 index 0000000..5d37fb2 --- /dev/null +++ b/apps/api/src/modules/health/health.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; + +import { HealthController } from './health.controller'; +import { HealthService } from './health.service'; + +@Module({ + controllers: [ HealthController ], + providers: [ HealthService ], +}) +export class HealthModule {} diff --git a/apps/api/src/modules/health/health.service.ts b/apps/api/src/modules/health/health.service.ts new file mode 100644 index 0000000..6b71803 --- /dev/null +++ b/apps/api/src/modules/health/health.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@nestjs/common'; +import { SupabaseService } from '@nxt/core'; + +@Injectable() +export class HealthService { + constructor(private readonly supabaseService: SupabaseService) {} + + async getHealth(): Promise<{ status: 'ok' }> { + await this.supabaseService.adminClient + .from('organizations') + .select('id') + .limit(1) + .then(response => this.supabaseService.handleResponse(response)); + + return { status: 'ok' }; + } +} diff --git a/apps/api/src/modules/user-admin/dto/create-agent.dto.ts b/apps/api/src/modules/user-admin/dto/create-agent.dto.ts new file mode 100644 index 0000000..78e988b --- /dev/null +++ b/apps/api/src/modules/user-admin/dto/create-agent.dto.ts @@ -0,0 +1,24 @@ +import { + IsEmail, + IsNotEmpty, + IsNumber, + IsOptional, + IsPhoneNumber, + IsString, +} from 'class-validator'; + +export class CreateAgentDto { + @IsString() + @IsNotEmpty() + full_name!: string; + + @IsPhoneNumber() + phone!: string; + + @IsEmail() + @IsOptional() + email?: string; + + @IsNumber() + grid_id!: number; +} diff --git a/apps/tiamat/src/modules/user-admin/dto/invite-member.dto.ts b/apps/api/src/modules/user-admin/dto/invite-member.dto.ts similarity index 65% rename from apps/tiamat/src/modules/user-admin/dto/invite-member.dto.ts rename to apps/api/src/modules/user-admin/dto/invite-member.dto.ts index e5e4f3f..5416d2e 100644 --- a/apps/tiamat/src/modules/user-admin/dto/invite-member.dto.ts +++ b/apps/api/src/modules/user-admin/dto/invite-member.dto.ts @@ -1,16 +1,19 @@ -import { Constants, MemberTypeEnum } from '@core/types/supabase-types'; import { - IsString, - IsNumber, - IsOptional, IsEmail, - IsNotEmpty, IsIn, + IsNotEmpty, + IsNumber, + IsOptional, + IsString, } from 'class-validator'; +import { + Constants, + type MemberTypeEnum, +} from '@nxt/core/types/supabase-types'; export class InviteMemberDto { @IsEmail() - email: string; + email!: string; @IsString() @IsOptional() @@ -18,13 +21,13 @@ export class InviteMemberDto { @IsString() @IsNotEmpty() - full_name: string; + full_name!: string; @IsNumber() - organization_id: number; + organization_id!: number; @IsIn(Constants.public.Enums.member_type_enum) - member_type: MemberTypeEnum; + member_type!: MemberTypeEnum; @IsNumber() @IsOptional() diff --git a/apps/api/src/modules/user-admin/dto/update-agent.dto.ts b/apps/api/src/modules/user-admin/dto/update-agent.dto.ts new file mode 100644 index 0000000..aa0e605 --- /dev/null +++ b/apps/api/src/modules/user-admin/dto/update-agent.dto.ts @@ -0,0 +1,24 @@ +import { + IsEmail, + IsNotEmpty, + IsNumber, + IsOptional, + IsPhoneNumber, + IsString, +} from 'class-validator'; + +export class UpdateAgentDto { + @IsNumber() + id!: number; + + @IsString() + @IsNotEmpty() + full_name!: string; + + @IsPhoneNumber() + phone!: string; + + @IsEmail() + @IsOptional() + email?: string; +} diff --git a/apps/tiamat/src/modules/user-admin/dto/update-customer.dto.ts b/apps/api/src/modules/user-admin/dto/update-customer.dto.ts similarity index 57% rename from apps/tiamat/src/modules/user-admin/dto/update-customer.dto.ts rename to apps/api/src/modules/user-admin/dto/update-customer.dto.ts index f525ea9..382ca2d 100644 --- a/apps/tiamat/src/modules/user-admin/dto/update-customer.dto.ts +++ b/apps/api/src/modules/user-admin/dto/update-customer.dto.ts @@ -1,12 +1,22 @@ -import { IsBoolean, IsEmail, IsLatitude, IsLongitude, IsNotEmpty, IsNumber, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; +import { + IsBoolean, + IsEmail, + IsLatitude, + IsLongitude, + IsNotEmpty, + IsNumber, + IsOptional, + IsPhoneNumber, + IsString, +} from 'class-validator'; export class UpdateCustomerDto { @IsNumber() - id: number; + id!: number; @IsString() @IsNotEmpty() - full_name: string; + full_name!: string; @IsPhoneNumber() @IsOptional() @@ -25,5 +35,5 @@ export class UpdateCustomerDto { longitude?: number; @IsBoolean() - is_hidden_from_reporting: boolean; + is_hidden_from_reporting!: boolean; } diff --git a/apps/tiamat/src/modules/user-admin/dto/update-member.dto.ts b/apps/api/src/modules/user-admin/dto/update-member.dto.ts similarity index 56% rename from apps/tiamat/src/modules/user-admin/dto/update-member.dto.ts rename to apps/api/src/modules/user-admin/dto/update-member.dto.ts index 26a5c30..91afadb 100644 --- a/apps/tiamat/src/modules/user-admin/dto/update-member.dto.ts +++ b/apps/api/src/modules/user-admin/dto/update-member.dto.ts @@ -1,34 +1,37 @@ -import { Constants, MemberTypeEnum } from '@core/types/supabase-types'; import { - IsString, - IsNumber, IsBoolean, + IsIn, IsNotEmpty, + IsNumber, IsOptional, - IsIn, + IsString, } from 'class-validator'; +import { + Constants, + type MemberTypeEnum, +} from '@nxt/core/types/supabase-types'; export class UpdateMemberDto { @IsNumber() - id: number; + id!: number; @IsString() @IsNotEmpty() - full_name: string; + full_name!: string; @IsIn(Constants.public.Enums.member_type_enum) - member_type: MemberTypeEnum; + member_type!: MemberTypeEnum; @IsNumber() - training_level: number; + training_level!: number; @IsNumber() @IsOptional() busy_commissioning_id?: number; @IsBoolean() - subscribed_to_telegram_revenue_notifications: boolean; + subscribed_to_telegram_revenue_notifications!: boolean; @IsBoolean() - hidden: boolean; + hidden!: boolean; } diff --git a/apps/tiamat/src/modules/user-admin/user-admin.controller.ts b/apps/api/src/modules/user-admin/user-admin.controller.ts similarity index 51% rename from apps/tiamat/src/modules/user-admin/user-admin.controller.ts rename to apps/api/src/modules/user-admin/user-admin.controller.ts index 13894c2..ef68de2 100644 --- a/apps/tiamat/src/modules/user-admin/user-admin.controller.ts +++ b/apps/api/src/modules/user-admin/user-admin.controller.ts @@ -1,13 +1,24 @@ -import { Controller, Post, UseGuards, Body, Delete, Param } from '@nestjs/common'; -import { AuthenticationGuard } from '../auth/authentication.guard'; -import { UserAdminService } from './user-admin.service'; -import { InviteMemberDto } from './dto/invite-member.dto'; -import { UpdateMemberDto } from './dto/update-member.dto'; -import { CreateAgentDto } from './dto/create-agent.dto'; -import { UpdateAgentDto } from './dto/update-agent.dto'; -import { CreateCustomerDto } from '@core/modules/customers/dto/create-customer.dto'; -import { UpdateCustomerDto } from './dto/update-customer.dto'; -import { CurrentUser, NxtSupabaseUser } from '../auth/nxt-supabase-user'; +import { + Body, + Controller, + Delete, + Param, + Post, + UseGuards, +} from '@nestjs/common'; +import { CreateCustomerDto } from '@nxt/core'; + +import { + CurrentUser, + type AuthenticatedUser, +} from '../auth/authenticated-user.js'; +import { AuthenticationGuard } from '../auth/authentication.guard.js'; +import { CreateAgentDto } from './dto/create-agent.dto.js'; +import { InviteMemberDto } from './dto/invite-member.dto.js'; +import { UpdateAgentDto } from './dto/update-agent.dto.js'; +import { UpdateCustomerDto } from './dto/update-customer.dto.js'; +import { UpdateMemberDto } from './dto/update-member.dto.js'; +import { UserAdminService } from './user-admin.service.js'; @UseGuards(AuthenticationGuard) @Controller('user-admin') @@ -17,7 +28,7 @@ export class UserAdminController { @Post('invite-member') inviteMember( @Body() body: InviteMemberDto, - @CurrentUser() user: NxtSupabaseUser, + @CurrentUser() user: AuthenticatedUser, ) { return this.service.inviteMember(body, user); } @@ -25,7 +36,7 @@ export class UserAdminController { @Post('update-member') updateMember( @Body() body: UpdateMemberDto, - @CurrentUser() user: NxtSupabaseUser, + @CurrentUser() user: AuthenticatedUser, ) { return this.service.updateMember(body, user); } @@ -33,7 +44,7 @@ export class UserAdminController { @Post('create-agent') createAgent( @Body() body: CreateAgentDto, - @CurrentUser() user: NxtSupabaseUser, + @CurrentUser() user: AuthenticatedUser, ) { return this.service.createAgent(body, user); } @@ -41,7 +52,7 @@ export class UserAdminController { @Post('update-agent') updateAgent( @Body() body: UpdateAgentDto, - @CurrentUser() user: NxtSupabaseUser, + @CurrentUser() user: AuthenticatedUser, ) { return this.service.updateAgent(body, user); } @@ -49,7 +60,7 @@ export class UserAdminController { @Post('create-customer') createCustomer( @Body() body: CreateCustomerDto, - @CurrentUser() user: NxtSupabaseUser, + @CurrentUser() user: AuthenticatedUser, ) { return this.service.createCustomer(body, user); } @@ -57,7 +68,7 @@ export class UserAdminController { @Post('update-customer') updateCustomer( @Body() body: UpdateCustomerDto, - @CurrentUser() user: NxtSupabaseUser, + @CurrentUser() user: AuthenticatedUser, ) { return this.service.updateCustomer(body, user); } @@ -65,24 +76,24 @@ export class UserAdminController { @Delete('member/:id') deleteMember( @Param('id') id: string, - @CurrentUser() user: NxtSupabaseUser, + @CurrentUser() user: AuthenticatedUser, ) { - return this.service.deleteAccount(parseInt(id), 'MEMBER', user); + return this.service.deleteAccount(parseInt(id, 10), 'MEMBER', user); } @Delete('agent/:id') deleteAgent( @Param('id') id: string, - @CurrentUser() user: NxtSupabaseUser, + @CurrentUser() user: AuthenticatedUser, ) { - return this.service.deleteAccount(parseInt(id), 'AGENT', user); + return this.service.deleteAccount(parseInt(id, 10), 'AGENT', user); } @Delete('customer/:id') deleteCustomer( @Param('id') id: string, - @CurrentUser() user: NxtSupabaseUser, + @CurrentUser() user: AuthenticatedUser, ) { - return this.service.deleteAccount(parseInt(id), 'CUSTOMER', user); + return this.service.deleteAccount(parseInt(id, 10), 'CUSTOMER', user); } } diff --git a/apps/api/src/modules/user-admin/user-admin.module.ts b/apps/api/src/modules/user-admin/user-admin.module.ts new file mode 100644 index 0000000..d6a0c24 --- /dev/null +++ b/apps/api/src/modules/user-admin/user-admin.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; + +import { UserAdminController } from './user-admin.controller.js'; +import { UserAdminService } from './user-admin.service.js'; + +@Module({ + controllers: [ UserAdminController ], + providers: [ UserAdminService ], + exports: [ UserAdminService ], +}) +export class UserAdminModule {} diff --git a/apps/api/src/modules/user-admin/user-admin.service.ts b/apps/api/src/modules/user-admin/user-admin.service.ts new file mode 100644 index 0000000..92be0a8 --- /dev/null +++ b/apps/api/src/modules/user-admin/user-admin.service.ts @@ -0,0 +1,484 @@ +import { + Injectable, + Logger, + NotFoundException, +} from '@nestjs/common'; +import { randomUUID } from 'node:crypto'; +import type { UserResponse } from '@supabase/supabase-js'; +import { pick } from 'ramda'; +import { + CreateCustomerDto, + SupabaseService, + throwSupabaseError, +} from '@nxt/core'; +import type { + AccountTypeEnum, + MemberTypeEnum, +} from '@nxt/core/types/supabase-types'; + +import type { AuthenticatedUser } from '../auth/authenticated-user.js'; +import type { CreateAgentDto } from './dto/create-agent.dto.js'; +import type { InviteMemberDto } from './dto/invite-member.dto.js'; +import type { UpdateAgentDto } from './dto/update-agent.dto.js'; +import type { UpdateCustomerDto } from './dto/update-customer.dto.js'; +import type { UpdateMemberDto } from './dto/update-member.dto.js'; + +interface SupabaseUserMetadata { + full_name: string; +} + +interface SupabaseAppMetadata { + account_id: number; + account_type: AccountTypeEnum; + member_type?: MemberTypeEnum; + organization_id: number; + grid_id?: number; +} + +interface AccountEmbed { + id: number; + supabase_id: string; + organization_id: number; +} + +const logger = new Logger('UserAdminService'); + +const handleUserResponse = (res: UserResponse) => { + const { data, error } = res; + if (error) { + throwSupabaseError(error, undefined, logger); + } + return data.user; +}; + +/** + * Privileged user administration (members, agents, customers). + * + * Whole-method admin client: Auth Admin APIs and follow-on DB writes cannot run + * under RLS. Callers may be bearer or API-key; API-key principals currently have + * no RLS-bound user client — see ADR-014 §5.3 (near-future). + * + * @TODO Dual-write risk: Auth (GoTrue) and Postgres are updated sequentially with + * no cross-service transaction (supabase-js cannot bundle them). A mid-flow failure + * can leave orphan Auth users, missing member/agent/customer rows, stale JWT + * metadata, or a soft-deleted account whose Auth user was not banned. Acceptable + * for low-frequency admin paths today; proportional follow-ups — await all Auth + * metadata updates (some create paths use `void`), and on create failure after + * Auth user exists compensate with `auth.admin.deleteUser`. Full resumable/saga + * machinery is not planned unless this starts failing in practice. + */ +@Injectable() +export class UserAdminService { + constructor(private readonly supabase: SupabaseService) {} + + async inviteMember( + { + email, + redirectTo, + full_name, + organization_id, + member_type, + busy_commissioning_id, + }: InviteMemberDto, + author: AuthenticatedUser, + ) { + await author.validate(); + + const user_metadata: SupabaseUserMetadata = { full_name: full_name.trim() }; + const user = await this.supabase.adminClient.auth.admin + .inviteUserByEmail(email, { data: user_metadata, redirectTo }) + .then(handleUserResponse); + + const account = await this.supabase.adminClient + .from('accounts') + .select('id') + .eq('supabase_id', user.id) + .single() + .then(this.supabase.handleSingle); + + const app_metadata: SupabaseAppMetadata = { + account_id: account.id, + account_type: 'MEMBER', + member_type, + organization_id, + }; + await this.supabase.adminClient.auth.admin + .updateUserById(user.id, { app_metadata }) + .then(handleUserResponse); + + const member = await this.supabase.adminClient + .from('members') + .insert({ + account_id: account.id, + member_type, + busy_commissioning_id, + }) + .select() + .single() + .then(this.supabase.handleSingle); + + const message = `${ author.full_name } invited a new member ${ full_name } (${ email }) with ${ member_type } role`; + void this.supabase.adminClient + .from('audits') + .insert({ + message, + author_id: author.account_id, + organization_id, + member_id: member.id, + }) + .then(this.supabase.handleResponse); + + return member; + } + + async updateMember( + { + id, + full_name, + member_type, + training_level, + busy_commissioning_id, + subscribed_to_telegram_revenue_notifications, + hidden, + }: UpdateMemberDto, + author: AuthenticatedUser, + ) { + await author.validate(); + + const member = await this.supabase.adminClient + .from('members') + .update({ + member_type, + training_level, + busy_commissioning_id, + subscribed_to_telegram_revenue_notifications, + hidden, + }) + .eq('id', id) + .select('id, account:accounts(id, supabase_id, organization_id)') + .single() + .then(this.supabase.handleSingle); + + const account = member.account as unknown as AccountEmbed; + const user_metadata: SupabaseUserMetadata = { full_name: full_name.trim() }; + const app_metadata: Partial = { member_type }; + await this.supabase.adminClient.auth.admin + .updateUserById(account.supabase_id, { user_metadata, app_metadata }) + .then(handleUserResponse); + + const message = `${ author.full_name } made updates to member ${ full_name }`; + void this.supabase.adminClient + .from('audits') + .insert({ + message, + author_id: author.account_id, + organization_id: account.organization_id, + member_id: member.id, + }) + .then(this.supabase.handleResponse); + + return member; + } + + async createAgent( + { phone, email, full_name, grid_id }: CreateAgentDto, + author: AuthenticatedUser, + ) { + await author.validate(); + + const { organization_id } = await this.supabase.adminClient + .from('grids') + .select('organization_id') + .eq('id', grid_id) + .single() + .then(this.supabase.handleSingle); + + const user_metadata: SupabaseUserMetadata = { full_name: full_name.trim() }; + const user = await this.supabase.adminClient.auth.admin + .createUser({ + phone, + email, + phone_confirm: true, + email_confirm: true, + user_metadata, + }) + .then(handleUserResponse); + + const account = await this.supabase.adminClient + .from('accounts') + .select('id') + .eq('supabase_id', user.id) + .single() + .then(this.supabase.handleSingle); + + const app_metadata: SupabaseAppMetadata = { + account_id: account.id, + account_type: 'AGENT', + organization_id, + grid_id, + }; + void this.supabase.adminClient.auth.admin.updateUserById(user.id, { + app_metadata, + }); + + const agent = await this.supabase.adminClient + .from('agents') + .insert({ + account_id: account.id, + grid_id, + }) + .select() + .single() + .then(this.supabase.handleSingle); + + await this.supabase.adminClient + .from('wallets') + .insert({ agent_id: agent.id }) + .then(this.supabase.handleResponse); + + const message = `${ author.full_name } created a new agent ${ full_name }`; + void this.supabase.adminClient + .from('audits') + .insert({ + message, + author_id: author.account_id, + organization_id, + grid_id, + agent_id: agent.id, + }) + .then(this.supabase.handleResponse); + + return agent; + } + + async updateAgent( + { id, full_name, phone, email }: UpdateAgentDto, + author: AuthenticatedUser, + ) { + await author.validate(); + + const agent = await this.supabase.adminClient + .from('agents') + .select('id, account:accounts(id, supabase_id, organization_id), grid_id') + .eq('id', id) + .single() + .then(this.supabase.handleSingle); + + const account = agent.account as unknown as AccountEmbed; + const user_metadata: SupabaseUserMetadata = { full_name: full_name.trim() }; + await this.supabase.adminClient.auth.admin + .updateUserById(account.supabase_id, { phone, email, user_metadata }) + .then(handleUserResponse); + + const message = `${ author.full_name } updated agent ${ full_name }`; + void this.supabase.adminClient + .from('audits') + .insert({ + message, + author_id: author.account_id, + organization_id: account.organization_id, + grid_id: agent.grid_id, + agent_id: agent.id, + }) + .then(this.supabase.handleResponse); + + return agent; + } + + async createCustomer( + createCustomerInput: CreateCustomerDto, + author: AuthenticatedUser, + ) { + await author.validate(); + + const { full_name, phone, email, grid_id } = createCustomerInput; + // Many customers cannot provide contact details — synthesize an email so Auth can create the user. + const resolvedEmail = email ? email : phone ? undefined : `${ randomUUID() }@gmail.com`; + + const { organization_id } = await this.supabase.adminClient + .from('grids') + .select('organization_id') + .eq('id', grid_id) + .single() + .then(this.supabase.handleSingle); + + const user_metadata: SupabaseUserMetadata = { full_name: full_name.trim() }; + const user = await this.supabase.adminClient.auth.admin + .createUser({ + phone, + email: resolvedEmail, + phone_confirm: true, + email_confirm: true, + user_metadata, + }) + .then(handleUserResponse); + + const account = await this.supabase.adminClient + .from('accounts') + .select('id') + .eq('supabase_id', user.id) + .single() + .then(this.supabase.handleSingle); + + const app_metadata: SupabaseAppMetadata = { + account_id: account.id, + account_type: 'CUSTOMER', + organization_id, + grid_id, + }; + void this.supabase.adminClient.auth.admin.updateUserById(user.id, { + app_metadata, + }); + + const customer = await this.supabase.adminClient + .from('customers') + .insert({ + account_id: account.id, + grid_id, + ...pick([ + 'latitude', + 'longitude', + 'is_hidden_from_reporting', + 'lives_primarily_in_the_community', + 'generator_owned', + 'gender', + 'total_connection_fee', + ], createCustomerInput), + }) + .select('*, account:accounts(*)') + .single() + .then(this.supabase.handleSingle); + + const message = `${ author.full_name } created a new customer ${ full_name }`; + void this.supabase.adminClient + .from('audits') + .insert({ + message, + author_id: author.account_id, + organization_id, + grid_id, + customer_id: customer.id, + }) + .then(this.supabase.handleResponse); + + return customer; + } + + async updateCustomer( + { + id, + full_name, + phone, + email, + latitude, + longitude, + is_hidden_from_reporting, + }: UpdateCustomerDto, + author: AuthenticatedUser, + ) { + await author.validate(); + + const customer = await this.supabase.adminClient + .from('customers') + .update({ latitude, longitude, is_hidden_from_reporting }) + .eq('id', id) + .select('id, grid_id, account:accounts(id, supabase_id, organization_id)') + .single() + .then(this.supabase.handleSingle); + + const account = customer.account as unknown as AccountEmbed; + const user_metadata: SupabaseUserMetadata = { full_name: full_name.trim() }; + await this.supabase.adminClient.auth.admin + .updateUserById(account.supabase_id, { phone, email, user_metadata }) + .then(handleUserResponse); + + const message = `${ author.full_name } updated customer ${ full_name }`; + void this.supabase.adminClient + .from('audits') + .insert({ + message, + author_id: author.account_id, + organization_id: account.organization_id, + grid_id: customer.grid_id, + customer_id: customer.id, + }) + .then(this.supabase.handleResponse); + + return customer; + } + + /** Soft-deletes the linked account and bans the Auth user. `id` is member/agent/customer id. */ + async deleteAccount( + id: number, + accountType: AccountTypeEnum, + author: AuthenticatedUser, + ) { + await author.validate(); + + const table = + accountType === 'AGENT' + ? 'agents' + : accountType === 'CUSTOMER' + ? 'customers' + : accountType === 'MEMBER' + ? 'members' + : null; + + if (!table) { + throw new Error(`Unsupported account type for delete: ${ accountType }`); + } + + const hasGrid = table === 'agents' || table === 'customers'; + + const { account_id, grid_id } = await this.supabase.adminClient + .from(table) + .select(hasGrid ? 'account_id, grid_id' : 'account_id') + .eq('id', id) + .single() + .then(this.supabase.handleSingle) + .then(row => row as unknown as { + account_id: number; + grid_id?: number | null; + }); + + const account = await this.supabase.adminClient + .from('accounts') + .update({ deleted_at: new Date().toISOString() }) + .eq('id', account_id) + .select('supabase_id, full_name, organization_id') + .single() + .then(this.supabase.handleSingle); + + if (!account.supabase_id) { + throw new NotFoundException('deleteAccount: account has no supabase_id'); + } + + await this.supabase.adminClient.auth.admin + .updateUserById(account.supabase_id, { ban_duration: '876000h' }) + .then(handleUserResponse); + + const entity = table.slice(0, -1); + const message = `${ author.full_name } deleted ${ entity } ${ account.full_name }`; + const entityIdField = + accountType === 'AGENT' + ? { agent_id: id } + : accountType === 'CUSTOMER' + ? { customer_id: id } + : accountType === 'MEMBER' + ? { member_id: id } + : {}; + + void this.supabase.adminClient + .from('audits') + .insert({ + message, + author_id: author.account_id, + organization_id: account.organization_id, + grid_id, + ...entityIdField, + }) + .then(this.supabase.handleResponse); + + return { accountType, full_name: account.full_name, deleted: true }; + } +} diff --git a/apps/api/test/e2e/auth.e2e.spec.ts b/apps/api/test/e2e/auth.e2e.spec.ts new file mode 100644 index 0000000..c166d40 --- /dev/null +++ b/apps/api/test/e2e/auth.e2e.spec.ts @@ -0,0 +1,92 @@ +import { + Injectable, + UnauthorizedException, + type INestApplication, + ValidationPipe, +} from '@nestjs/common'; +import { Test, type TestingModule } from '@nestjs/testing'; +import { PassportModule, PassportStrategy } from '@nestjs/passport'; +import { GlobalSupabaseModule } from '@nxt/core'; +import { Strategy as BearerStrategy } from 'passport-http-bearer'; +import request from 'supertest'; + +import { ApiKeyStrategy } from '../../src/modules/auth/api-key.strategy.js'; +import { AuthController } from '../../src/modules/auth/auth.controller.js'; +import { AuthenticationGuard } from '../../src/modules/auth/authentication.guard.js'; +import { + getLocalSupabaseEnv, + SEEDED_PLATFORM_API_KEY, + SEEDED_PLATFORM_SUPABASE_ID, +} from '../helpers/local-supabase-env.js'; + +/** + * Registers Passport name `supabase` so AuthenticationGuard's dual strategy + * list can run. Always fails — this suite only exercises X-API-KEY. + * + * Why not AppModule? Booting AuthModule pulls SupabaseStrategy → ESM-only + * `jose`, which Jest (CJS) cannot load without a heavier transform harness. + * Thin slice is intentional until a full-app e2e harness exists; do not + * proliferate thin modules as the default pattern. + */ +@Injectable() +class StubSupabaseStrategy extends PassportStrategy(BearerStrategy, 'supabase') { + constructor() { + super(); + } + + validate(): never { + throw new UnauthorizedException('Stub bearer strategy — not used in this suite'); + } +} + +const localEnv = getLocalSupabaseEnv(); +const describeE2e = localEnv ? describe : describe.skip; + +describeE2e('GET /auth/me (e2e — X-API-KEY)', () => { + let app: INestApplication; + let moduleRef: TestingModule; + + beforeAll(async () => { + moduleRef = await Test.createTestingModule({ + imports: [ PassportModule, GlobalSupabaseModule ], + controllers: [ AuthController ], + providers: [ AuthenticationGuard, ApiKeyStrategy, StubSupabaseStrategy ], + }).compile(); + + app = moduleRef.createNestApplication(); + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + transformOptions: { enableImplicitConversion: false }, + }), + ); + await app.init(); + }); + + afterAll(async () => { + await app?.close(); + await moduleRef?.close(); + }); + + it('returns the seeded platform principal for X-API-KEY', async () => { + const res = await request(app.getHttpServer()) + .get('/auth/me') + .set('X-API-KEY', SEEDED_PLATFORM_API_KEY) + .expect(200); + + expect(res.body).toMatchObject({ + email: 'superadmin@nxt-platform.com', + full_name: 'Platform Superadmin', + account_type: 'MEMBER', + member_type: 'SUPERADMIN', + organization_id: 1, + supabase_id: SEEDED_PLATFORM_SUPABASE_ID, + }); + expect(res.body.account_id).toBeGreaterThan(0); + }); + + it('rejects the request when X-API-KEY is missing', async () => { + await request(app.getHttpServer()).get('/auth/me').expect(401); + }); +}); diff --git a/apps/api/test/helpers/local-supabase-env.ts b/apps/api/test/helpers/local-supabase-env.ts new file mode 100644 index 0000000..4b44822 --- /dev/null +++ b/apps/api/test/helpers/local-supabase-env.ts @@ -0,0 +1,27 @@ +/** + * Local Supabase env helpers for integration / e2e suites under `apps/api/test/`. + * + * Nx loads `apps/api/.env` then workspace `.env` for `nx test*` the same way as + * `nx serve api` (project wins on conflicts). Returns null when keys are missing + * so stack-dependent suites can skip instead of failing a stack-free default run. + */ + +/** Seeded in `supabase/seed.sql` — platform SUPERADMIN (org 1). */ +export const SEEDED_PLATFORM_API_KEY = 'dev-api-key-platform-superadmin'; + +export const SEEDED_PLATFORM_SUPABASE_ID = + 'a0000000-0000-4000-8000-000000000001'; + +export interface LocalSupabaseEnv { + readonly url: string; + readonly secretKey: string; +} + +export function getLocalSupabaseEnv(): LocalSupabaseEnv | null { + const url = process.env.SUPABASE_URL?.trim(); + const secretKey = process.env.SUPABASE_SECRET_KEY?.trim(); + if (!url || !secretKey) { + return null; + } + return { url, secretKey }; +} diff --git a/apps/api/test/integration/auth/api-key.strategy.integration.spec.ts b/apps/api/test/integration/auth/api-key.strategy.integration.spec.ts new file mode 100644 index 0000000..b6b7e3d --- /dev/null +++ b/apps/api/test/integration/auth/api-key.strategy.integration.spec.ts @@ -0,0 +1,52 @@ +import { UnauthorizedException } from '@nestjs/common'; +import { Test, type TestingModule } from '@nestjs/testing'; +import { GlobalSupabaseModule } from '@nxt/core'; + +import { ApiKeyStrategy } from '../../../src/modules/auth/api-key.strategy.js'; +import { + getLocalSupabaseEnv, + SEEDED_PLATFORM_API_KEY, + SEEDED_PLATFORM_SUPABASE_ID, +} from '../../helpers/local-supabase-env.js'; + +const localEnv = getLocalSupabaseEnv(); +const describeIntegration = localEnv ? describe : describe.skip; + +describeIntegration('ApiKeyStrategy (integration — local Supabase + seed)', () => { + let moduleRef: TestingModule; + let strategy: ApiKeyStrategy; + + beforeAll(async () => { + moduleRef = await Test.createTestingModule({ + imports: [ GlobalSupabaseModule ], + providers: [ ApiKeyStrategy ], + }).compile(); + + strategy = moduleRef.get(ApiKeyStrategy); + }); + + afterAll(async () => { + await moduleRef?.close(); + }); + + it('resolves the seeded platform API key to AuthenticatedUser claims', async () => { + const user = await strategy.validate(SEEDED_PLATFORM_API_KEY); + + expect(user).toMatchObject({ + email: 'superadmin@nxt-platform.com', + full_name: 'Platform Superadmin', + account_type: 'MEMBER', + member_type: 'SUPERADMIN', + organization_id: 1, + supabase_id: SEEDED_PLATFORM_SUPABASE_ID, + }); + expect(user.account_id).toBeGreaterThan(0); + expect(typeof user.validate).toBe('function'); + }); + + it('rejects an unknown API key', async () => { + await expect(strategy.validate('not-a-real-api-key')).rejects.toBeInstanceOf( + UnauthorizedException, + ); + }); +}); diff --git a/apps/api/test/unit/helpers/local-supabase-env.spec.ts b/apps/api/test/unit/helpers/local-supabase-env.spec.ts new file mode 100644 index 0000000..61ecc4c --- /dev/null +++ b/apps/api/test/unit/helpers/local-supabase-env.spec.ts @@ -0,0 +1,33 @@ +import { getLocalSupabaseEnv } from '../../helpers/local-supabase-env.js'; + +describe('getLocalSupabaseEnv', () => { + const originalEnv = { ...process.env }; + + afterEach(() => { + process.env = { ...originalEnv }; + }); + + it('returns null when SUPABASE_URL is missing', () => { + delete process.env.SUPABASE_URL; + process.env.SUPABASE_SECRET_KEY = 'test-secret'; + + expect(getLocalSupabaseEnv()).toBeNull(); + }); + + it('returns null when SUPABASE_SECRET_KEY is missing', () => { + process.env.SUPABASE_URL = 'http://127.0.0.1:54321'; + delete process.env.SUPABASE_SECRET_KEY; + + expect(getLocalSupabaseEnv()).toBeNull(); + }); + + it('returns trimmed url and secret when both are set', () => { + process.env.SUPABASE_URL = ' http://127.0.0.1:54321 '; + process.env.SUPABASE_SECRET_KEY = ' test-secret '; + + expect(getLocalSupabaseEnv()).toEqual({ + url: 'http://127.0.0.1:54321', + secretKey: 'test-secret', + }); + }); +}); diff --git a/apps/api/tsconfig.app.json b/apps/api/tsconfig.app.json new file mode 100644 index 0000000..751825c --- /dev/null +++ b/apps/api/tsconfig.app.json @@ -0,0 +1,31 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "types": [ + "node" + ], + "rootDir": "src", + "tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo", + "target": "es2021" + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "out-tsc", + "dist", + "jest.config.ts", + "jest.config.cts", + "src/**/*.spec.ts", + "src/**/*.test.ts", + "eslint.config.js", + "eslint.config.cjs", + "eslint.config.mjs" + ], + "references": [ + { + "path": "../../libs/core/tsconfig.lib.json" + } + ] +} diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json new file mode 100644 index 0000000..63dbe35 --- /dev/null +++ b/apps/api/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/apps/api/tsconfig.spec.json b/apps/api/tsconfig.spec.json new file mode 100644 index 0000000..4f85359 --- /dev/null +++ b/apps/api/tsconfig.spec.json @@ -0,0 +1,24 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./out-tsc/jest", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "jest.config.ts", + "jest.config.cts", + "jest.integration.config.cts", + "jest.e2e.config.cts", + "jest.shared.cjs", + "src/**/*.d.ts", + "test/**/*.ts" + ], + "references": [ + { + "path": "./tsconfig.app.json" + } + ] +} diff --git a/apps/api/webpack.config.js b/apps/api/webpack.config.js new file mode 100644 index 0000000..44108b1 --- /dev/null +++ b/apps/api/webpack.config.js @@ -0,0 +1,39 @@ +const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin'); +const nodeExternals = require('webpack-node-externals'); +const { join } = require('path'); + +module.exports = { + output: { + path: join(__dirname, 'dist'), + clean: true, + ...(process.env.NODE_ENV !== 'production' && { + devtoolModuleFilenameTemplate: '[absolute-resource-path]', + }), + }, + externals: [ + nodeExternals({ + modulesFromFile: { + fileName: join(__dirname, 'package.json'), + }, + }), + ], + plugins: [ + new NxAppWebpackPlugin({ + target: 'node', + compiler: 'tsc', + main: './src/main.ts', + tsConfig: './tsconfig.app.json', + assets: [ + // Bundled default config (ADR-007 decision 4) — ships next to main.js so a built + // image boots in evaluation mode with zero env. + { input: '../..', glob: 'config.default.json', output: '.' }, + ], + externalDependencies: 'none', + mergeExternals: true, + optimization: false, + outputHashing: 'none', + generatePackageJson: false, + sourceMap: true, + }), + ], +}; diff --git a/apps/loch/src/modules/lost-revenue/lost-revenue.module.ts b/apps/loch/src/modules/lost-revenue/lost-revenue.module.ts deleted file mode 100644 index 792a66f..0000000 --- a/apps/loch/src/modules/lost-revenue/lost-revenue.module.ts +++ /dev/null @@ -1,7 +0,0 @@ -// import { Module } from '@nestjs/common'; -// import { LostRevenueService } from './lost-revenue.service'; - -// @Module({ -// providers: [ LostRevenueService ], -// }) -// export class LostRevenueModule {} diff --git a/apps/loch/src/modules/lost-revenue/lost-revenue.service.ts b/apps/loch/src/modules/lost-revenue/lost-revenue.service.ts deleted file mode 100644 index 9f8c680..0000000 --- a/apps/loch/src/modules/lost-revenue/lost-revenue.service.ts +++ /dev/null @@ -1,107 +0,0 @@ -// For now we are not using this service. Commenting out until further notice. - -// import { SupabaseService } from '@core/modules/supabase.module'; -// import { IssueStatusEnum } from '@core/types/supabase-types'; -// import { mapAsyncSequential } from '@helpers/promise-helpers'; - -// import { Injectable } from '@nestjs/common'; -// import { Cron } from '@nestjs/schedule'; -// import { InjectDataSource } from '@nestjs/typeorm'; -// import moment from 'moment'; -// import { DataSource } from 'typeorm'; - -// @Injectable() -// export class LostRevenueService { - -// constructor( -// private readonly dataSource: DataSource, -// @InjectDataSource('timescale') -// protected readonly timescale: DataSource, -// private readonly supabaseService: SupabaseService, -// ) {} - -// isLostRevenueCalculationRunning = false; - -// // run this every night at 3 am -// @Cron('0 3 * * *', { disabled: process.env.NXT_ENV !== 'production' }) -// async run() { -// if (this.isLostRevenueCalculationRunning) return; -// this.isLostRevenueCalculationRunning = true; - -// const size = 1000; -// let page = 0; -// let openIssueStats: any[]; -// let issuesToUpdate: any[] = []; - -// try { -// const now = moment(); -// const oneWeekAgo = moment(now).subtract(1, 'week'); -// const consumptions: any[] = await this.findAvgHourlyConsumptionInAllGridsByStartAndEndGroupedByGridAndMeterType(oneWeekAgo, now); -// // console.info('consumptions '); -// // console.info(consumptions); -// do { -// issuesToUpdate = []; -// // if there are some open issues, then find whatm, in the last week, was the average hourly consumption -// // for HPS/FS meters -// openIssueStats = await this.findIssueStatsByIssueStatusAndMeterIsNotNull('OPEN', size, page++ * size); -// if (openIssueStats.length < 1) { -// break; -// } - -// for (const issueStat of openIssueStats) { -// const consumption: any = consumptions.find(({ grid_id, meter_type }) => -// grid_id === issueStat.grid_id && issueStat.meter_type === meter_type); - -// if (!consumption) { -// // console.info('nothing found for'); -// // console.info(o); -// continue; -// } - -// issuesToUpdate.push({ -// id: issueStat.id, -// estimated_lost_revenue: (Number(consumption.avg) || 0) * (issueStat.hours || 0), -// }); -// } - -// const { handleResponse, adminClient: supabase } = this.supabaseService; -// await mapAsyncSequential(async ({ id, estimated_lost_revenue }) => { -// return supabase.from('issues').update({ estimated_lost_revenue }).eq('id', id).then(handleResponse); -// })(issuesToUpdate); - -// } while (openIssueStats.length > 0); -// } -// catch (err) { -// console.error(err); -// } -// finally { -// this.isLostRevenueCalculationRunning = false; -// } -// } - -// async findAvgHourlyConsumptionInAllGridsByStartAndEndGroupedByGridAndMeterType(start: moment.Moment, end: moment.Moment): Promise { - -// const res = await this.timescale.query(query, [ -// start.format('YYYY-MM-DD HH:mm:ss'), -// end.format('YYYY-MM-DD HH:mm:ss'), -// ]); - -// return res; -// } - -// async findIssueStatsByIssueStatusAndMeterIsNotNull(issueStatus: IssueStatusEnum, limit: number, offset: number) { -// const issueStats = await this.dataSource.query(query, [ -// issueStatus, -// limit, -// offset, -// ], -// ); - -// // format to number -// for (const issueStat of issueStats) { -// issueStat.hours = Number(issueStat.hours); -// } - -// return issueStats; -// } -// } diff --git a/apps/tiamat/src/modules/accounts/accounts.module.ts b/apps/tiamat/src/modules/accounts/accounts.module.ts deleted file mode 100644 index 71430cd..0000000 --- a/apps/tiamat/src/modules/accounts/accounts.module.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Global, Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { Account } from '@core/modules/accounts/entities/account.entity'; -import { AccountsService } from '@core/modules/accounts/accounts.service'; - -@Global() -@Module({ - imports: [ TypeOrmModule.forFeature([ Account ]) ], - providers: [ AccountsService ], - exports: [ AccountsService ], -}) -export class AccountsModule { } diff --git a/apps/tiamat/src/modules/api-keys/api-keys.module.ts b/apps/tiamat/src/modules/api-keys/api-keys.module.ts deleted file mode 100644 index 1636857..0000000 --- a/apps/tiamat/src/modules/api-keys/api-keys.module.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Global, Module } from '@nestjs/common'; -import { ApiKeysService } from '@core/modules/api-keys/api-keys.service'; -import { ApiKey } from '@core/modules/api-keys/entities/api-key.entity'; -import { TypeOrmModule } from '@nestjs/typeorm'; - -@Global() -@Module({ - imports: [ TypeOrmModule.forFeature([ ApiKey ]) ], - providers: [ ApiKeysService ], - exports: [ ApiKeysService ], -}) -export class ApiKeysModule {} diff --git a/apps/tiamat/src/modules/auth/api-key.strategy.ts b/apps/tiamat/src/modules/auth/api-key.strategy.ts deleted file mode 100644 index ba9ee92..0000000 --- a/apps/tiamat/src/modules/auth/api-key.strategy.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { HeaderAPIKeyStrategy } from 'passport-headerapikey'; -import { PassportStrategy } from '@nestjs/passport'; -import { Injectable, UnauthorizedException } from '@nestjs/common'; -import { ApiKeysService } from '@core/modules/api-keys/api-keys.service'; -import { ApiKey } from '@core/modules/api-keys/entities/api-key.entity'; -import { NxtSupabaseUser } from './nxt-supabase-user'; -import { NXT_ORG_ID } from '@core/constants'; - -@Injectable() -export class ApiKeyStrategy extends PassportStrategy(HeaderAPIKeyStrategy) { - constructor(private readonly apiKeysService: ApiKeysService) { - super({ header: 'X-API-KEY', prefix: '' }, false); - } - - public validate = async (apiKey: string): Promise => { - const key: ApiKey = await this.apiKeysService.findByKeyAndIsLocked(apiKey, false); - if (!key?.account || key.account.deleted_at) - throw new UnauthorizedException('The API key used does not have a corresponding account'); - - const { - id, - full_name, - email, - member: { member_type }, - organization, - supabase_id, - } = key.account; - - return { - email, - full_name, - account_type: 'MEMBER', - member_type, - account_id: id, - organization_id: organization.id, - is_nxt_grid_member: organization.id === NXT_ORG_ID, - supabase_id, - account: key.account, - async validate() { return {}; }, // Fake validation method - }; - }; -} diff --git a/apps/tiamat/src/modules/auth/auth.controller.ts b/apps/tiamat/src/modules/auth/auth.controller.ts deleted file mode 100644 index f3980c8..0000000 --- a/apps/tiamat/src/modules/auth/auth.controller.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Controller, Get, UseGuards } from '@nestjs/common'; -import { AuthService } from './auth.service'; -import { CurrentUser, NxtSupabaseUser } from './nxt-supabase-user'; -import { AuthenticationGuard } from './authentication.guard'; - -@UseGuards(AuthenticationGuard) -@Controller('auth') -export class AuthController { - constructor(private readonly authService: AuthService, - ) { } - - @Get('grafana-token') - getGrafanaToken( - @CurrentUser() user: NxtSupabaseUser, - ) { - return this.authService.generateAccessToken(user.account); - } -} diff --git a/apps/tiamat/src/modules/auth/auth.module.ts b/apps/tiamat/src/modules/auth/auth.module.ts deleted file mode 100644 index b5e5c87..0000000 --- a/apps/tiamat/src/modules/auth/auth.module.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Global, Module } from '@nestjs/common'; -import { AuthService } from './auth.service'; -import { AccountsModule } from '../accounts/accounts.module'; -import { PassportModule } from '@nestjs/passport'; -import { JwtModule } from '@nestjs/jwt'; -import { ApiKeyStrategy } from './api-key.strategy'; -import { AuthController } from './auth.controller'; -import { SupabaseStrategy } from './supabase.strategy'; - -@Global() -@Module({ - imports: [ - AccountsModule, - PassportModule, - JwtModule.register({ - privateKey: process.env.PRIVATE_KEY, - signOptions: { - expiresIn: process.env.NXT_JWT_DURATION, - algorithm: 'RS256', - }, - }) ], - providers: [ - AuthService, - ApiKeyStrategy, - SupabaseStrategy, - ], - controllers: [ AuthController ], - exports: [ AuthService ], -}) -export class AuthModule { } - diff --git a/apps/tiamat/src/modules/auth/auth.service.ts b/apps/tiamat/src/modules/auth/auth.service.ts deleted file mode 100644 index c5d43ea..0000000 --- a/apps/tiamat/src/modules/auth/auth.service.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { JwtService } from '@nestjs/jwt'; -import { Account } from '@core/modules/accounts/entities/account.entity'; - -@Injectable() -export class AuthService { - constructor(private jwtService: JwtService) { } - - async generateAccessToken(account: Account) { - const payload = { username: account.email, sub: String(account.id) }; - const refresh_token = this.jwtService.sign(payload, { expiresIn: '30d' }); - - return { - access_token: this.jwtService.sign({ - username: account.email, - sub: String(account.id), - }), - refresh_token: refresh_token, - }; - } -} diff --git a/apps/tiamat/src/modules/auth/authentication.guard.ts b/apps/tiamat/src/modules/auth/authentication.guard.ts deleted file mode 100644 index 4cba19a..0000000 --- a/apps/tiamat/src/modules/auth/authentication.guard.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; -import { AuthGuard } from '@nestjs/passport'; -import { Observable } from 'rxjs'; - -@Injectable() -export class AuthenticationGuard extends AuthGuard([ 'supabase', 'headerapikey' ]) implements CanActivate { - canActivate(context: ExecutionContext): boolean | Promise | Observable { - return super.canActivate(context); - } - - // @TODO-GQL :: Check if we even need this!? - // getRequest(context: ExecutionContext) { - // if (context.getType() === 'http') { - // return context.switchToHttp().getRequest(); - // } - // } -} diff --git a/apps/tiamat/src/modules/auth/nxt-supabase-user.ts b/apps/tiamat/src/modules/auth/nxt-supabase-user.ts deleted file mode 100644 index 16849e2..0000000 --- a/apps/tiamat/src/modules/auth/nxt-supabase-user.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { createParamDecorator, ExecutionContext } from '@nestjs/common'; -import { Account } from '@core/modules/accounts/entities/account.entity'; -import { SupabaseClient, PostgrestError } from '@supabase/supabase-js'; -import { AccountTypeEnum, Database, MemberTypeEnum } from '@core/types/supabase-types'; - -export interface NxtSupabaseUser { - email: string; - full_name: string; - account_type: AccountTypeEnum; - member_type: MemberTypeEnum; - account_id: number; - organization_id: number; - is_nxt_grid_member: boolean; - supabase_id: string; - supabase?: { - client: SupabaseClient - handleResponse({ data, error, status }: { data: T, error: PostgrestError, status: number }): T - } - account?: Account - validate(): any -} - -export const CurrentUser = createParamDecorator( - (_data: unknown, context: ExecutionContext): NxtSupabaseUser => { - return context.getArgs()[0].user; - }, -); diff --git a/apps/tiamat/src/modules/auth/supabase.strategy.ts b/apps/tiamat/src/modules/auth/supabase.strategy.ts deleted file mode 100644 index 6d78662..0000000 --- a/apps/tiamat/src/modules/auth/supabase.strategy.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { Inject, Injectable, UnauthorizedException } from '@nestjs/common'; -import { PassportStrategy } from '@nestjs/passport'; -import { Strategy } from 'passport-http-bearer'; -import { verify, JwtPayload } from 'jsonwebtoken'; -import { createClient, PostgrestError } from '@supabase/supabase-js'; -import { NxtSupabaseUser } from './nxt-supabase-user'; -import { throwSupabaseError } from '@core/modules/supabase.module'; -import { AccountsService } from '@core/modules/accounts/accounts.service'; -import { NXT_ORG_ID } from '@core/constants'; - -import type { Database } from '@core/types/supabase-types-adjusted'; - -const { SUPABASE_API_URL, SUPABASE_ANON_KEY, SUPABASE_JWT_SECRET } = process.env; - -@Injectable() -export class SupabaseStrategy extends PassportStrategy(Strategy, 'supabase') { - constructor( - @Inject(AccountsService) - private readonly accountsService: AccountsService, - ) { super(); } - - async validate(token: string): Promise { - let decoded: JwtPayload; - try { - decoded = verify(token, SUPABASE_JWT_SECRET) as JwtPayload; - } - catch(err) { - console.error('Error verifying Supabase token', err); - throw new UnauthorizedException('Invalid or expired token'); - } - if(!decoded?.sub) throw new UnauthorizedException('No UID in token'); - - const { - email, - sub, - user_metadata: { full_name }, - app_metadata: { account_type, member_type, account_id, organization_id }, - } = decoded; - if(!account_id) throw new UnauthorizedException('This auth.user does not have a corresponding account'); - - const supabaseClient = createClient( - SUPABASE_API_URL, - SUPABASE_ANON_KEY, - { global: { headers: { Authorization: `Bearer ${ token }` } } }, - ); - - // @TEMPORARY :: Our old system is expecting an Account object, - // so we tack on the Account for now until it is not needed anymore - const account = await this.accountsService.findOne(account_id); - if(!account) throw new UnauthorizedException('No account for this user'); - - return { - email, - full_name, - account_type, - member_type, - account_id, - organization_id, - is_nxt_grid_member: organization_id === NXT_ORG_ID, - supabase_id: sub, - supabase: { - client: supabaseClient, - handleResponse({ data, error, status }: { data: T, error: PostgrestError, status: number }): T { - if (error) throwSupabaseError(error, status); - return data; - }, - }, - validate({ organization_id }: { organization_id?: number } = {}) { - if(organization_id) { - // @TODO :: Match against this.organization_id (let all NXT Grid pass) - console.info('Validate against organization id', organization_id); - } - return this.supabase.client.auth - .getUser() - .then(({ data, error }) => { - if(error) throwSupabaseError(error, 401); - if (!data?.user) throw new UnauthorizedException('No valid user data found'); - return data.user; - }) - ; - }, - account, - }; - } -} diff --git a/apps/tiamat/src/modules/chirpstack/chirpstack.service.ts b/apps/tiamat/src/modules/chirpstack/chirpstack.service.ts deleted file mode 100644 index e72c134..0000000 --- a/apps/tiamat/src/modules/chirpstack/chirpstack.service.ts +++ /dev/null @@ -1,202 +0,0 @@ -import moment from 'moment'; -import { SupabaseService } from '@core/modules/supabase.module'; -import { HttpService } from '@nestjs/axios'; -import { HttpException, Injectable } from '@nestjs/common'; -import { CreateOrderDto } from '../orders/dto/create-order.dto'; - -@Injectable() -export class ChirpStackService { - constructor( - private readonly httpService: HttpService, - private readonly supabaseService: SupabaseService, - ) {} - - // Processes incoming messages from acrel meters. - // 1. It looks for the corresponding meter; - // 2. It updates said meter; - // 3. It sends a copy of the data to yeti, so it gets added to historical data recording - async processIncomingLorawanMessageForAcrelProfile(body: any) { - const { handleResponse, adminClient: supabase } = this.supabaseService; - const externalReference = body.deviceInfo?.devEui; - - // Will throw already if no corresponding meter is not found - const device = await supabase - .from('devices') - .select('*, meter:meters(*, wallet:wallets!meter_id(*), connections(*, customers(*, wallet:wallets(*))))') - .eq('external_reference', externalReference) - .single() - .then(handleResponse); //TODO: there might be a poblem with this, since it's going to make the failure silent... - - if (!device.id) { - console.error(`Could not find device with external reference ${ externalReference }. Returning...`); - return; - } - - // Add a log so we have a reference to original data - await supabase - .from('device_logs') - .insert({ - device_id: device.id, - message: JSON.stringify(body), - }) - .then(handleResponse) - ; - - // TODO: update the meter object - - // Update the device object - await supabase - .from('devices') - .update({ values: body?.object?.data }) - .eq('external_reference', externalReference) - .single() - .then(handleResponse); - - const data = body.object.data; - const toSendToYeti = { - device_id: device.id, - created_at: moment.utc(body.time), //The meter is sending time in the local timezone - timestamp: moment.utc(body.time), - data, //This is the structure specified at decoder level, so it's going to change for every device - meta: {}, //TODO: add meta? - }; - - await this.checkForKwhAndProduceOrderIfNecessary(device.id, data); - - // Send a copy to yeti to insert into timescale - this.httpService - .axiosRef - .post(`${ process.env.YETI_API }/device-data-sink/ingest`, toSendToYeti) - .catch(err => { - console.error('Error sending to Yeti', err); - }); - } - - // Every time new data comes in, we check whether more than 30 mins have elapsed from the last update. - // If that's the case, then we check whether the meter is postpaid. So, as energy is consumed, credit - // is going to be removed from the customer wallet, and moved into the meter wallet, which is a leaf - // wallet in our credit tree. - async checkForKwhAndProduceOrderIfNecessary(deviceId: number, event: any) { - // If the fields are not available, then skip the whole thing - // TODO: check for the correct fields - if (typeof event.EPa !== 'number') return; - - const { handleResponse, adminClient: supabase } = this.supabaseService; - - // TODO: add the check about whether the meter is a postpaid meter - // For now, since all acrel meters are postpaid, we are not going to have any condition in place - - // Will throw already if no corresponding meter is not found - const device = await supabase - .from('devices') - .select('*, meter:meters(*, wallet:wallets!meter_id(*), connection:connections(*, customer:customers(*, grid:grids(*), wallet:wallets(*))))') - .eq('id', deviceId) - .single() - .then(handleResponse); //TODO: there might be a poblem with this, since it's going to make the failure silent... - - // TODO: isn't this supposed to work? - const meter = device.meter; - if(!meter) { - console.error(`[CHIRPSTACK SERVICE] Trying to update meter for a device that doesn't have one. Id: ${ device.id }, external reference: ${ device.external_reference }`); - return; - } - const thirtyMinsAgo = moment().subtract(30, 'minutes'); - - // If the credit of the meter was updated last time more than 30 mins ago or is null - if (!meter.pulse_counter_kwh_updated_at || moment(meter.pulse_counter_kwh_updated_at).isBefore(thirtyMinsAgo)) { - // Find the wallet of the customer to send credit from - const customerWalletId = meter.connection?.customer?.wallet?.id; - // Find the wallet of the leaf meter to send credit to - - if (!customerWalletId) { - console.error(`Could not find a customer wallet for meter with id ${ customerWalletId }. Returning...`); - return; - } - - // In order to find the amount, we fetch need to know the delta - const oldPulseCounterKwh = meter.pulse_counter_kwh || 0; //If it's null, we mark it as zero - const kwh = event.EPa - oldPulseCounterKwh; - - // Update the kwh counter - await supabase - .from('meters') - .update({ pulse_counter_kwh_updated_at: moment().toISOString(), pulse_counter_kwh: event.EPa }) - .eq('id', meter.id); - - // TODO: determine exactly where the tariff is defined - // If a meter has a specific tariff, then we are going to use that. - // Otherwise we'll use the grid tariff - let tariff; let amount; - if (typeof meter.kwh_tariff == 'number') { - tariff = meter.kwh_tariff; - - if (tariff !== 0) amount = kwh / tariff; - else throw new HttpException(`Tariff for meter ${ meter.external_reference } is 0`, 500); - } - else { - tariff = meter.connection.customer.grid.kwh_tariff_essential_service; - amount = kwh / tariff; - } - - // Find the - const newOrder: CreateOrderDto = { - sender_wallet_id: customerWalletId, - receiver_wallet_id: meter.wallet.id, - payment_channel: null, - amount: amount, - currency: 'NGN', - order_status: 'INITIALISED', - }; - - console.info('Created order via prepostpaid'); - console.info(newOrder); - // // The order service will take care of processing it automatically - // await this.orderService.create(newOrder); - } - } - - // Processes incoming messages from acrel meters. - // 1. It looks for the corresponding meter; - // 2. It updates said meter; - // 3. It sends a copy of the data to yeti, so it gets added to historical data recording - async processIncomingLorawanMessageForMilesightProfile(body: any) { - const { handleResponse, adminClient: supabase } = this.supabaseService; - const externalReference = body.deviceInfo?.devEui; - - // Will throw already if no corresponding meter is not found - const device = await supabase - .from('devices') - .select() - .eq('external_reference', externalReference) - .single() - .then(handleResponse); - - // Add a log so we have a reference to original data - await supabase - .from('device_logs') - .insert({ - device_id: device.id, - message: JSON.stringify(body), - }) - .then(handleResponse) - ; - - const toSendToYeti = { - device_id: device.id, - created_at: moment.utc(body.time), //The meter is sending time in the local timezone - timestamp: moment.utc(body.time), - data: body.object, //This is the structure specified at decoder level, so it's going to change for every device - meta: {}, //TODO: add meta? - }; - - // Send a copy to yeti to insert into timescale - await this.httpService - .axiosRef - .post(`${ process.env.YETI_API }/device-data-sink/ingest`, toSendToYeti) - .catch(err => { - console.error('Error sending to Yeti', err); - }); - - return; - } -} diff --git a/apps/tiamat/src/modules/organizations/organizations.controller.ts b/apps/tiamat/src/modules/organizations/organizations.controller.ts deleted file mode 100644 index f573d6a..0000000 --- a/apps/tiamat/src/modules/organizations/organizations.controller.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Controller, /* Post, Body, */ UseGuards/* , Get, Param, ParseIntPipe */ } from '@nestjs/common'; -import { OrganizationsService } from './organizations.service'; -import { AuthenticationGuard } from '../auth/authentication.guard'; - -@UseGuards(AuthenticationGuard) -@Controller('organizations') -export class OrganizationsController { - constructor(protected readonly service: OrganizationsService) {} - - // This is used by Flow XO to look for organzations - // @Get(':id') - // getOrganization( - // @Param('id', ParseIntPipe) id: number, - // ) { - // // @TODO :: Check if we need this one - // console.info(` - // ================================================================= - // ================================================================= - // GET ORGANIZATION ${ id } IS CALLED - // ================================================================= - // ================================================================= - // `); - // return this.service.findOne(id); - // } -} diff --git a/apps/tiamat/src/modules/organizations/organizations.module.ts b/apps/tiamat/src/modules/organizations/organizations.module.ts deleted file mode 100644 index 3cf1585..0000000 --- a/apps/tiamat/src/modules/organizations/organizations.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Global, Module } from '@nestjs/common'; -import { OrganizationsService } from './organizations.service'; -import { OrganizationsController } from './organizations.controller'; - -@Global() -@Module({ - providers: [ OrganizationsService ], - controllers: [ OrganizationsController ], -}) -export class OrganizationsModule { } diff --git a/apps/tiamat/src/modules/organizations/organizations.service.ts b/apps/tiamat/src/modules/organizations/organizations.service.ts deleted file mode 100644 index f2788d4..0000000 --- a/apps/tiamat/src/modules/organizations/organizations.service.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { SupabaseService } from '@core/modules/supabase.module'; - -@Injectable() -export class OrganizationsService { - constructor( - private readonly supabaseService: SupabaseService, - ) {} - async findOne(id: number) { - return this.supabaseService.adminClient - .from('organizations') - .select('*, wallet:wallets(*)') - .eq('id', id) - .maybeSingle() - .then(this.supabaseService.handleResponse) - ; - } -} diff --git a/apps/tiamat/src/modules/user-admin/dto/create-agent.dto.ts b/apps/tiamat/src/modules/user-admin/dto/create-agent.dto.ts deleted file mode 100644 index ee62a31..0000000 --- a/apps/tiamat/src/modules/user-admin/dto/create-agent.dto.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { IsEmail, IsNotEmpty, IsNumber, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; - -export class CreateAgentDto { - @IsString() - @IsNotEmpty() - full_name: string; - - @IsPhoneNumber() - phone: string; - - @IsEmail() - @IsOptional() - email?: string; - - @IsNumber() - grid_id: number; -} diff --git a/apps/tiamat/src/modules/user-admin/dto/update-agent.dto.ts b/apps/tiamat/src/modules/user-admin/dto/update-agent.dto.ts deleted file mode 100644 index 070ab22..0000000 --- a/apps/tiamat/src/modules/user-admin/dto/update-agent.dto.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { IsEmail, IsNotEmpty, IsNumber, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; - -export class UpdateAgentDto { - @IsNumber() - id: number; - - @IsString() - @IsNotEmpty() - full_name: string; - - @IsPhoneNumber() - phone: string; - - @IsEmail() - @IsOptional() - email?: string; -} diff --git a/apps/tiamat/src/modules/user-admin/user-admin.module.ts b/apps/tiamat/src/modules/user-admin/user-admin.module.ts deleted file mode 100644 index cb193ee..0000000 --- a/apps/tiamat/src/modules/user-admin/user-admin.module.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Global, Module } from '@nestjs/common'; -import { UserAdminController } from './user-admin.controller'; -import { UserAdminService } from './user-admin.service'; - -@Global() -@Module({ - controllers: [ UserAdminController ], - providers: [ UserAdminService ], - exports: [ UserAdminService ], -}) -export class UserAdminModule { } diff --git a/apps/tiamat/src/modules/user-admin/user-admin.service.ts b/apps/tiamat/src/modules/user-admin/user-admin.service.ts deleted file mode 100644 index 6d698b4..0000000 --- a/apps/tiamat/src/modules/user-admin/user-admin.service.ts +++ /dev/null @@ -1,518 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { pick } from 'ramda'; -import { v4 as uuidv4 } from 'uuid'; -import { SupabaseService, throwSupabaseError } from '@core/modules/supabase.module'; -import { InviteMemberDto } from './dto/invite-member.dto'; -import { UpdateMemberDto } from './dto/update-member.dto'; -import { CreateAgentDto } from './dto/create-agent.dto'; -import { UpdateAgentDto } from './dto/update-agent.dto'; -import { CreateCustomerDto } from '@core/modules/customers/dto/create-customer.dto'; -import { UpdateCustomerDto } from './dto/update-customer.dto'; -import { NxtSupabaseUser } from '../auth/nxt-supabase-user'; -import { AccountTypeEnum, MemberTypeEnum } from '@core/types/supabase-types'; -import { UserResponse } from '@supabase/supabase-js'; - -export interface SupabaseUserMetadata { - full_name: string -} - -export interface SupabaseAppMetadata { - account_id: number - account_type: AccountTypeEnum - member_type?: MemberTypeEnum - organization_id: number - grid_id?: number -} - -const handleUserResponse = (res: UserResponse) => { - const { data, error } = res; - if (error) { - console.error('error handling User Admin', res); - throwSupabaseError(error); - } - return data.user; -}; - -@Injectable() -export class UserAdminService { - constructor(private readonly supabase: SupabaseService) { - // const password = 'PASSWORD'; - - // Superadmin - // this.createTestUser({ email: 'bobby.bol@nxtgrid.co', full_name: 'Bobby Bol', organization_id: 2, member_type: MemberType.SUPERADMIN, password }); - // Developer - // this.createTestUser({ email: 'bobby.bol+developer@nxtgrid.co', full_name: 'Bobby Bol Developer', organization_id: 9, member_type: MemberType.DEVELOPER, password }); - // Tech - // this.createTestUser({ email: 'bobby.bol+tech@nxtgrid.co', full_name: 'Bobby Bol Tech', organization_id: 9, member_type: MemberType.TECH, password }); - // Agent - // this.createTestUser({ isAgent: true, email: 'bobby.bol+agent3@nxtgrid.co', phone: '+31622748374', full_name: 'Bobby Bol Agent', grid_id: 5, password }); - } - - // async createTestUser(options) { - // if(process.env.NXT_ENV === 'production') return; - // const { email, phone, full_name, password, member_type, organization_id, grid_id, isAgent } = options; - - // let account_id; - - // if(isAgent) { - // const agent = await this.createAgent({ phone, email, full_name, grid_id }); - // account_id = agent.account_id; - // } - // else { - // const member = await this.inviteMember({ - // email, - // full_name, - // organization_id, - // member_type, - // redirectTo: '', - // }); - // account_id = member.account_id; - // } - - // const [ account ] = await this.supabase.adminClient - // .from('accounts') - // .select('supabase_id') - // .eq('id', account_id) - // .then(this.supabase.handleResponse) - // ; - - // await this.supabase.adminClient.auth.admin.updateUserById(account.supabase_id, { - // password, - // email_confirm: true, - // phone_confirm: true, - // }); - // } - - async inviteMember({ - email, - redirectTo, - full_name, - organization_id, - member_type, - busy_commissioning_id, - }: InviteMemberDto, author: NxtSupabaseUser) { - // 0) Since we're going to use the admin client, we want to validate the authoring user - await author.validate(); - - // 1) Create a Supabase user which also creates an account - const user_metadata: SupabaseUserMetadata = { full_name: full_name.trim() }; - const user = await this.supabase.adminClient.auth.admin - .inviteUserByEmail(email, { data: user_metadata, redirectTo }) - .then(handleUserResponse) - ; - - // 2 Fetch the automatically created account - const account = await this.supabase.adminClient - .from('accounts') - .select('id') - .eq('supabase_id', user.id) - .single() - .then(this.supabase.handleResponse) - ; - - // 3) Now we have to update the Supabase user to store some ACL RLS properties - const app_metadata: SupabaseAppMetadata = { - account_id: account.id, - account_type: 'MEMBER', - member_type, - organization_id, - }; - await this.supabase.adminClient.auth.admin - .updateUserById(user.id, { app_metadata }) - .then(handleUserResponse) - ; - - // 4) Create a NXT Grid member tied to that account - const member = await this.supabase.adminClient - .from('members') - .insert({ - account_id: account.id, - member_type, - busy_commissioning_id, - }) - .select() - .single() - .then(this.supabase.handleResponse) - ; - - // 5) Create audit - const message = `${ author.full_name } invited a new member ${ full_name } (${ email }) with ${ member_type } role`; - this.supabase.adminClient - .from('audits') - .insert({ - message, - author_id: author.account_id, - organization_id, - member_id: member.id, - }) - .then(this.supabase.handleResponse) - ; - - return member; - } - - async updateMember({ - id, - full_name, - member_type, - training_level, - busy_commissioning_id, - subscribed_to_telegram_revenue_notifications, - hidden, - }: UpdateMemberDto, author: NxtSupabaseUser) { - // 0) Since we're going to use the admin client, we want to validate the authoring user - await author.validate(); - - // 1) Update and get the member with account - const member = await this.supabase.adminClient - .from('members') - .update({ member_type, training_level, busy_commissioning_id, subscribed_to_telegram_revenue_notifications, hidden }) - .eq('id', id) - .select('id, account:accounts(id, supabase_id, organization_id)') - .single() - .then(this.supabase.handleResponse) - ; - - // 2) Update the Supabase User (which automatically updates account too) - const user_metadata: SupabaseUserMetadata = { full_name: full_name.trim() }; - const app_metadata: Partial = { member_type }; - await this.supabase.adminClient.auth.admin - .updateUserById(member.account.supabase_id, { user_metadata, app_metadata }) - .then(handleUserResponse) - ; - - // 3) Create audit - const message = `${ author.full_name } made updates to member ${ full_name }`; - this.supabase.adminClient - .from('audits') - .insert({ - message, - author_id: author.account_id, - organization_id: member.account.organization_id, - member_id: member.id, - }) - .then(this.supabase.handleResponse) - ; - - return member; - } - - async createAgent({ phone, email, full_name, grid_id }: CreateAgentDto, author: NxtSupabaseUser) { - // 0) Since we're going to use the admin client, we want to validate the authoring user - await author.validate(); - - // 1) We create agents for grids but we need organization_id for RLS - const { organization_id } = await this.supabase.adminClient - .from('grids') - .select('organization_id') - .eq('id', grid_id) - .single() - .then(this.supabase.handleResponse) - ; - - // 2) Create a Supabase user which also creates an account - const user_metadata: SupabaseUserMetadata = { full_name: full_name.trim() }; - const user = await this.supabase.adminClient.auth.admin - .createUser({ - phone, - email, - phone_confirm: true, - email_confirm: true, - user_metadata, - }) - .then(handleUserResponse) - ; - - // 3) Fetch the automatically created account - const account = await this.supabase.adminClient - .from('accounts') - .select('id') - .eq('supabase_id', user.id) - .single() - .then(this.supabase.handleResponse) - ; - - // 4) Now we have to update the Supabase user to store some ACL RLS properties - const app_metadata: SupabaseAppMetadata = { - account_id: account.id, - account_type: 'AGENT', - organization_id, - grid_id, - }; - this.supabase.adminClient.auth.admin - .updateUserById(user.id, { app_metadata }) - ; - - // 5) Create an Agent tied to that account - const agent = await this.supabase.adminClient - .from('agents') - .insert({ - account_id: account.id, - grid_id, - }) - .select() - .single() - .then(this.supabase.handleResponse) - ; - - // 6) Create a Wallet for the agent - await this.supabase.adminClient - .from('wallets') - .insert({ agent_id: agent.id }) - .then(this.supabase.handleResponse) - ; - - // 7) Create audit - const message = `${ author.full_name } created a new agent ${ full_name }`; - this.supabase.adminClient - .from('audits') - .insert({ - message, - author_id: author.account_id, - organization_id, - grid_id, - agent_id: agent.id, - }) - .then(this.supabase.handleResponse) - ; - - return agent; - } - - async updateAgent({ id, full_name, phone, email }: UpdateAgentDto, author: NxtSupabaseUser) { - // 0) Since we're going to use the admin client, we want to validate the authoring user - await author.validate(); - - // 1) Get the agent for some extra properties - const agent = await this.supabase.adminClient - .from('agents') - .select('id, account:accounts(id, supabase_id, organization_id), grid_id') - .eq('id', id) - .single() - .then(this.supabase.handleResponse) - ; - - // 2) Do the actual update - const user_metadata: SupabaseUserMetadata = { full_name: full_name.trim() }; - await this.supabase.adminClient.auth.admin - .updateUserById(agent.account.supabase_id, { phone, email, user_metadata }) - .then(handleUserResponse) - ; - - // 3) Create audit - const message = `${ author.full_name } updated agent ${ full_name }`; - this.supabase.adminClient - .from('audits') - .insert({ - message, - author_id: author.account_id, - organization_id: agent.account.organization_id, - grid_id: agent.grid_id, - agent_id: agent.id, - }) - .then(this.supabase.handleResponse) - ; - - return agent; - } - - async createCustomer(createCustomerInput: CreateCustomerDto, author: NxtSupabaseUser) { - // 0) Since we're going to use the admin client, we want to validate the authoring user - await author.validate(); - - const { full_name, phone, email, grid_id } = createCustomerInput; - // If no email or phone number is passed (many customers can't provide a phone number), - // we make a fake email instead so we can still create the account - const _email = email ? email : phone ? undefined : `${ uuidv4() }@gmail.com`; - - // 1) We create customers for grids but we need organization_id for RLS - const { organization_id } = await this.supabase.adminClient - .from('grids') - .select('organization_id') - .eq('id', grid_id) - .single() - .then(this.supabase.handleResponse) - ; - - // 2) Create a Supabase user which also creates an account - const user_metadata: SupabaseUserMetadata = { full_name: full_name.trim() }; - const user = await this.supabase.adminClient.auth.admin - .createUser({ - phone, - email: _email, - phone_confirm: true, - email_confirm: true, - user_metadata, - }) - .then(handleUserResponse) - ; - - // 3) Fetch the automatically created account - const account = await this.supabase.adminClient - .from('accounts') - .select('id') - .eq('supabase_id', user.id) - .single() - .then(this.supabase.handleResponse) - ; - - // 4) Now we have to update the Supabase user to store some ACL RLS properties - const app_metadata: SupabaseAppMetadata = { - account_id: account.id, - account_type: 'CUSTOMER', - organization_id, - grid_id, - }; - this.supabase.adminClient.auth.admin - .updateUserById(user.id, { app_metadata }) - ; - - // 5) Create a customer tied to that account - const customer = await this.supabase.adminClient - .from('customers') - .insert({ - account_id: account.id, - grid_id, - ...pick([ - 'latitude', - 'longitude', - 'is_hidden_from_reporting', - 'lives_primarily_in_the_community', - 'generator_owned', - 'gender', - 'total_connection_fee', - ], createCustomerInput), - }) - .select('*, account:accounts(*)') - .single() - .then(this.supabase.handleResponse) - ; - - // 6) Create audit - const message = `${ author.full_name } created a new customer ${ full_name }`; - this.supabase.adminClient - .from('audits') - .insert({ - message, - author_id: author.account_id, - organization_id, - grid_id, - customer_id: customer.id, - }) - .then(this.supabase.handleResponse) - ; - - return customer; - } - - async updateCustomer({ - id, - full_name, - phone, - email, - latitude, - longitude, - is_hidden_from_reporting, - }: UpdateCustomerDto, author: NxtSupabaseUser) { - // 0) Since we're going to use the admin client, we want to validate the authoring user - await author.validate(); - - // 1) Update and get the customer with account - const customer = await this.supabase.adminClient - .from('customers') - .update({ latitude, longitude, is_hidden_from_reporting }) - .eq('id', id) - .select('id, grid_id, account:accounts(id, supabase_id, organization_id)') - .single() - .then(this.supabase.handleResponse) - ; - - // 2) Update the Supabase User (which automatically updates account too) - const user_metadata: SupabaseUserMetadata = { full_name: full_name.trim() }; - await this.supabase.adminClient.auth.admin - .updateUserById(customer.account.supabase_id, { phone, email, user_metadata }) - .then(handleUserResponse) - ; - - // 3) Create audit - const message = `${ author.full_name } updated customer ${ full_name }`; - this.supabase.adminClient - .from('audits') - .insert({ - message, - author_id: author.account_id, - organization_id: customer.account.organization_id, - grid_id: customer.grid_id, - customer_id: customer.id, - }) - .then(this.supabase.handleResponse) - ; - - return customer; - } - - // The ID passed here is the agent, customer, or member ID - async deleteAccount(id: number, accountType: AccountTypeEnum, author: NxtSupabaseUser) { - // 0) Since we're going to use the admin client, we want to validate the authoring user - await author.validate(); - - const table = - accountType === 'AGENT' ? 'agents' : - accountType === 'CUSTOMER' ? 'customers' : - accountType === 'MEMBER' ? 'members' : - null; - - const hasGrid = [ 'agents', 'customers' ].includes(table); - - // 1) Get appropriate account_id - const { account_id, grid_id } = await this.supabase.adminClient - .from(table) - .select(`account_id ${ hasGrid ? ', grid_id' : '' }`) - .eq('id', id) - .single() - .then(this.supabase.handleResponse) as { - account_id: number; - grid_id?: number | null; - } - ; - - // 3) Soft delete the account - const { supabase_id, full_name, organization_id } = await this.supabase.adminClient - .from('accounts') - .update({ deleted_at: new Date().toISOString() }) - .eq('id', account_id) - .select('supabase_id, full_name, organization_id') - .single() - .then(this.supabase.handleResponse) - ; - - // 4) Ban the Supabase user for a 100 years - await this.supabase.adminClient.auth.admin - .updateUserById(supabase_id, { ban_duration: '876000h' }) - .then(handleUserResponse) - ; - - const entity = table.slice(0, -1); - - // 5) Create audit - const message = `${ author.full_name } deleted ${ entity } ${ full_name }`; - const entityIdField = - accountType === 'AGENT' ? { agent_id: id } : - accountType === 'CUSTOMER' ? { customer_id: id } : - accountType === 'MEMBER' ? { member_id: id } : - {}; - this.supabase.adminClient - .from('audits') - .insert({ - message, - author_id: author.account_id, - organization_id, - grid_id, - ...entityIdField, - }) - .then(this.supabase.handleResponse) - ; - - return { accountType, full_name, deleted: true }; - } -} diff --git "a/apps/tiamat/src/modules/\344\270\200demo/demo.controller.ts" "b/apps/tiamat/src/modules/\344\270\200demo/demo.controller.ts" deleted file mode 100644 index 2d12030..0000000 --- "a/apps/tiamat/src/modules/\344\270\200demo/demo.controller.ts" +++ /dev/null @@ -1,26 +0,0 @@ -// import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common'; -// import { DemoService } from './demo.service'; -// import { AuthenticationGuard } from '../auth/authentication.guard'; -// import { CurrentUser, NxtSupabaseUser } from '../auth/nxt-supabase-user'; - -// @Controller('demo') -// export class DemoController { -// constructor( -// protected readonly demoService: DemoService, -// ) { } - -// @UseGuards(AuthenticationGuard) -// @Get('reset') -// async demoReset( -// @CurrentUser() user: NxtSupabaseUser, -// ): Promise { -// this.demoService.resetDemoGrid(user); -// return { response: 'ok' }; -// } - -// @Post('log-payload') -// logPayload(@Body() body) { -// console.info(body); -// return 'logged your test'; -// } -// } diff --git "a/apps/tiamat/src/modules/\344\270\200demo/demo.module.ts" "b/apps/tiamat/src/modules/\344\270\200demo/demo.module.ts" deleted file mode 100644 index 0d5af04..0000000 --- "a/apps/tiamat/src/modules/\344\270\200demo/demo.module.ts" +++ /dev/null @@ -1,13 +0,0 @@ -// DEMO Module is deprecated unless someone asks for it - -// import { Global, Module } from '@nestjs/common'; -// import { DemoService } from './demo.service'; -// import { DemoController } from './demo.controller'; - -// @Global() -// @Module({ -// providers: [ DemoService ], -// exports: [ DemoService ], -// controllers: [ DemoController ], -// }) -// export class DemoModule { } diff --git "a/apps/tiamat/src/modules/\344\270\200demo/demo.service.ts" "b/apps/tiamat/src/modules/\344\270\200demo/demo.service.ts" deleted file mode 100644 index 01a768c..0000000 --- "a/apps/tiamat/src/modules/\344\270\200demo/demo.service.ts" +++ /dev/null @@ -1,208 +0,0 @@ -// import { Injectable, NotFoundException } from '@nestjs/common'; -// import { Cron } from '@nestjs/schedule'; -// import { OrdersService } from '../orders/orders.service'; -// import { SupabaseService } from '@core/modules/supabase.module'; -// import { UserAdminService } from '../user-admin/user-admin.service'; -// import { NxtSupabaseUser } from '../auth/nxt-supabase-user'; - -// import { GridsService } from '../grids/grids.service'; - -// const DEMO_GRID_NAME = 'Demo Community'; -// const DUMPSTER_WALLET_IDENTIFIER = 'CUSTOMER_DEMO_RESET_SYSTEM'; - -// @Injectable() -// export class DemoService { -// isDemoResetRunning = false; - -// constructor( -// private readonly gridsService: GridsService, -// private readonly ordersService: OrdersService, -// private readonly userAdminService: UserAdminService, -// private readonly supabaseService: SupabaseService, -// ) {} - -// async resetDemoGrid(author: NxtSupabaseUser): Promise { -// if (this.isDemoResetRunning) { -// console.info('[DEMO RESET] Skipping...'); -// return; -// } - -// try { -// this.isDemoResetRunning = true; -// console.info('[DEMO RESET] Running...'); - -// const demoGrid = await this.supabaseService.adminClient -// .from('grids') -// .select(` -// id, -// organization:organizations( -// id, -// wallet:wallets!organization_id( -// id, -// balance -// ) -// ) -// `) -// .eq('name', DEMO_GRID_NAME) -// .maybeSingle() -// .then(this.supabaseService.handleResponse) -// ; - -// if (!demoGrid) throw new NotFoundException(`[DEMO RESET] Could not find ${ DEMO_GRID_NAME }`); -// console.log(demoGrid); - - -// /** -// * Clear the meters -// **/ - -// const meterIds = await this.supabaseService.adminClient -// .from('meters') -// .select(` -// id, -// external_reference, -// dcu:dcus!inner( -// grid_id -// ) -// `) -// .eq('dcu.grid_id', demoGrid.id) -// .then(this.supabaseService.handleResponse) -// .then(res => { -// console.log(res); -// return res; -// }) -// .then(meters => meters.map(({ id }) => id)) -// ; - -// console.log(meterIds); - -// // Remove all meter assignments -// // await this.supabaseService.adminClient -// // .from('meters') -// // .update({ -// // connection_id: null, -// // // @TOMMASO ? -// // // dcu_id: null, -// // // pole_id: null, -// // // rls_grid_id: null, -// // // rls_organization_id: null, -// // }) -// // .in('id', meterIds) -// // .then(this.supabaseService.handleResponse) -// // ; - - -// /** -// * Clear the customers -// **/ -// // @TOMMASO :: Do we want to unplug the connection or delete customers and agents? - -// // const customers: Customer[] = await this.customerssssService.findBysGridIdAndIsDeleted(demoGrid.id, false); -// const customerIds = await this.supabaseService.adminClient -// .from('customers') -// .select(` -// id, -// account:accounts!inner( -// deleted_at -// ) -// `) -// .eq('grid_id', demoGrid.id) -// .is('account.deleted_at', null) -// .then(this.supabaseService.handleResponse) -// // .then(customers => customers.map(({ id }) => id)) -// ; - -// console.log(customerIds); -// // We want to soft delete them, deleting their account -// // for(const id of customerIds) { -// // await this.userAdminService.deleteAccount(id, 'CUSTOMER', author); - -// // @TOMMASO :: Do we also want to 'unplug' the grid? -// // await this.customerssssService.update(id, { id, grid_id: null }); -// // } - - -// /** -// * Clear the agents -// **/ -// // const agents: Agent[] = await this.agentService.findByGridId(demoGrid.id); -// const agentIds = await this.supabaseService.adminClient -// .from('agents') -// .select(` -// id, -// account:accounts!inner( -// deleted_at -// ) -// `) -// .eq('grid_id', demoGrid.id) -// .is('account.deleted_at', null) -// .then(this.supabaseService.handleResponse) -// ; - -// console.log(agentIds); - -// // remove agents from grid - -// return; - -// /** -// * Emtpy the organization wallet into dumpster wallet -// * (Dumpster wallet exists to keep track of akk the money ever spent as part of demoes) -// **/ -// // @TOMMASO :: What about the (customer,) agent, meters wallets? -// // const wallet: Wallet = await this.walletsService.findByOrganizationId(demoGrid.organization.id); -// const { organization } = demoGrid; -// if(!organization) { -// console.warn(`[DEMO RESET] No organization for ${ DEMO_GRID_NAME }, aborting early...`); -// return; -// } -// const { wallet } = organization; -// if(!wallet) { -// console.warn(`[DEMO RESET] No organization wallet for ${ DEMO_GRID_NAME }, aborting early...`); -// return; -// } -// if(wallet.balance === 0) return; - -// // We send all the money belonging to the demo organization back to the -// // dumpster wallet that keeps track of all the money ever spent as part of demoes -// const dumpsterWallet = await this.supabaseService.adminClient -// .from('wallets') -// .select('id') -// .eq('identifier', DUMPSTER_WALLET_IDENTIFIER) -// .maybeSingle() -// .then(this.supabaseService.handleResponse) -// ; - -// if (!dumpsterWallet) { -// console.warn('[DEMO RESET] Couldn\'t find the demo dumpster wallet, aborting early...'); -// return; -// } - -// await this.ordersService.create({ -// sender_wallet_id: wallet.id, -// receiver_wallet_id: dumpsterWallet.id, -// amount: wallet.balance, -// currency: 'NGN', -// payment_channel: 'AYRTON', -// }, author); -// } -// finally { -// console.info('[DEMO RESET] Completed'); -// this.isDemoResetRunning = false; -// } -// } - -// // @TOMMASO :: What? -// // we need this cron to -// @Cron('*/4 * * * *') //every 4 mins, since the -// async updateIsHpsOnDataOfDemoGrid() { -// const grid = await this.gridsService.findByName(DEMO_GRID_NAME); -// if(!grid) return; - -// await this.gridsssService.update(grid.id, { -// id: grid.id, -// is_hps_on: true, -// is_hps_on_updated_at: new Date(), -// }); -// } -// } diff --git "a/apps/tiamat/src/modules/\344\270\200directive-watchdog-sessions/directive-watchdog-sessions.controller.ts" "b/apps/tiamat/src/modules/\344\270\200directive-watchdog-sessions/directive-watchdog-sessions.controller.ts" deleted file mode 100644 index a056bf4..0000000 --- "a/apps/tiamat/src/modules/\344\270\200directive-watchdog-sessions/directive-watchdog-sessions.controller.ts" +++ /dev/null @@ -1,18 +0,0 @@ -// import { Controller, UseGuards } from '@nestjs/common'; -// import { DirectiveWatchdogSessionsService } from './directive-watchdog-sessions.service'; -// import { AuthenticationGuard } from '../auth/authentication.guard'; -// @UseGuards(AuthenticationGuard) -// @Controller('directive-watchdog-sessions') -// export class DirectiveWatchdogSessionsController { -// constructor( -// private readonly directiveWatchdogSessionsService: DirectiveWatchdogSessionsService, -// ) {} - -// // @Post('debug-meter') -// // setMeterToDebug(@Body('meterToDebug') meterToDebug: string) { -// // if (!meterToDebug.startsWith('47001')) { -// // throw new HttpException(`Invalid meter ext reference ${ meterToDebug }, must start with 47001`, 400); -// // } -// // return this.directiveWatchdogSessionsService.setMeterToDebug(meterToDebug); -// // } -// } diff --git "a/apps/tiamat/src/modules/\344\270\200directive-watchdog-sessions/directive-watchdog-sessions.module.ts" "b/apps/tiamat/src/modules/\344\270\200directive-watchdog-sessions/directive-watchdog-sessions.module.ts" deleted file mode 100644 index 5e8421a..0000000 --- "a/apps/tiamat/src/modules/\344\270\200directive-watchdog-sessions/directive-watchdog-sessions.module.ts" +++ /dev/null @@ -1,13 +0,0 @@ -// import { Global, Module } from '@nestjs/common'; -// import { DirectiveWatchdogSessionsService } from './directive-watchdog-sessions.service'; -// import { TypeOrmModule } from '@nestjs/typeorm'; -// import { DirectiveWatchdogSession } from '@core/modules/directive-watchdog-sessions/entities/directive-watchdog-session.entity'; -// import { DirectiveWatchdogSessionsController } from './directive-watchdog-sessions.controller'; -// @Global() -// @Module({ -// imports: [ TypeOrmModule.forFeature([ DirectiveWatchdogSession ]) ], -// providers: [ DirectiveWatchdogSessionsService ], -// exports: [ DirectiveWatchdogSessionsService ], -// controllers: [ DirectiveWatchdogSessionsController ], -// }) -// export class DirectiveWatchdogSessionsModule {} diff --git "a/apps/tiamat/src/modules/\344\270\200directive-watchdog-sessions/directive-watchdog-sessions.service.ts" "b/apps/tiamat/src/modules/\344\270\200directive-watchdog-sessions/directive-watchdog-sessions.service.ts" deleted file mode 100644 index 8f5a720..0000000 --- "a/apps/tiamat/src/modules/\344\270\200directive-watchdog-sessions/directive-watchdog-sessions.service.ts" +++ /dev/null @@ -1,124 +0,0 @@ -// import { Injectable } from '@nestjs/common'; -// import { InjectRepository } from '@nestjs/typeorm'; -// import { Repository } from 'typeorm'; -// import { v4 as uuidv4 } from 'uuid'; -// import moment from 'moment'; - -// import { DirectiveWatchdogSession } from '@core/modules/directive-watchdog-sessions/entities/directive-watchdog-session.entity'; -// import { Meter } from '@core/modules/meters/entities/meter.entity'; - -// import { CreateDirectiveWatchdogSessionInput } from '@core/modules/directive-watchdog-sessions/dto/create-directive-watchdog-session.input'; - -// import { DirectiveWatchdogSessionsService as CoreDirectiveWatchdogService } from '@core/modules/directive-watchdog-sessions/directive-watchdog-sessions.service'; -// import { MetersService } from '@tiamat/modules/meters/meters.service'; - -// @Injectable() -// export class DirectiveWatchdogSessionsService extends CoreDirectiveWatchdogService { -// isSessionRunning = false; - -// constructor( -// @InjectRepository(DirectiveWatchdogSession) -// protected readonly directiveWatchdogSessionRepository: Repository, -// protected readonly metersService: MetersService, -// ) { -// super(directiveWatchdogSessionRepository); -// } - -// async create(createDwInput: CreateDirectiveWatchdogSessionInput) { -// const qb = this.directiveWatchdogSessionRepository.createQueryBuilder('directive_watchdog_sessions'); - -// await qb.insert() -// .values(createDwInput) -// .orIgnore() //we ignore if there are conflicts, as it means that another node attempted the same thing with the same value -// .execute(); - -// return this.findByIdentifier(createDwInput.identifier); -// } - -// // Only focuses on the grids where there are no upcoming fs control rules in the coming hour, or where -// // there has been an fs control rule in the last hour. This is because we want to give time to the -// // rules to be executed (it may take up to 30 minutes for them to complete sometimes). - -// // Given a list of eligible grids, we find all the meters with a PLS or ON/OFF mismatch that are -// // not in manual mode, and create the corresponding directives -// // @Cron('0 * * * *', { disabled: process.env.NXT_ENV !== 'production' }) -// async run() { -// if (this.isSessionRunning) return; -// this.isSessionRunning = true; - -// try { -// // First we create the watchdog session. -// const createdAt = moment() -// .minute(0) -// .second(0) -// .millisecond(0); -// const newWatchdog: CreateDirectiveWatchdogSessionInput = { identifier: createdAt.unix().toString() }; -// const session = await this.create(newWatchdog); - -// // The watchdog should not involve all grids, but only the ones where there are no upcoming fs control rules -// // in the next hour and no fs rule executions for the past hour -// // const gridsWithNoFsActivityInTheLastHour: Grid[] = await this.getGridsWithNoActivityIntheLastHours(1); -// const size = 100; -// let metersToFix: Meter[] = []; - -// do { -// const uuid = uuidv4(); //Changes at every page, since we do not want to refetch the same meters at every iteration -// metersToFix = await this.metersService.lockNextPageOfMetersWithDiscrepancy(uuid, createdAt, size); -// if (metersToFix.length < 1) break; - -// await this.processMeters(metersToFix, session); - -// } while (metersToFix.length > 0); -// } -// catch (err) { -// console.error(err); -// } -// finally { -// this.isSessionRunning = false; -// } -// } - -// processMeters(meters: Meter[], session: DirectiveWatchdogSession) { -// console.warn('WATCHDOG NOT IMPLEMENTED', session); -// // const directives = meters -// // .filter(meter => meter.is_on !== meter.should_be_on || meter.power_limit_should_be !== meter.power_limit) -// // .map(meter => { -// // if (meter.is_on !== meter.should_be_on) { -// // return { -// // directive_status: 'PENDING', -// // directive_type: (meter.should_be_on) ? 'ON' : 'OFF', -// // meter_id: meter.id, -// // directive_watchdog_session: session, -// // directive_priority: 600, -// // }; -// // } -// // else { // set the correct power limit -// // return { -// // directive_status: 'PENDING', -// // directive_type: 'PLS', -// // meter_id: meter.id, -// // power_limit_should_be: meter.power_limit_should_be, -// // directive_watchdog_session: session, -// // directive_priority: 600, -// // }; -// // } -// // }); - -// // return this.directiveRouterService.create(directives); -// } - -// // We want to try to fix HPS meters even though they -// // might be in a grid that is about to have a directive batch. -// // private async getGridsWithNoActivityIntheLastHours(hours: number): Promise { -// // const now = moment(); -// // const oneHourAgo = moment(now).subtract(hours, 'hour'); - -// // const upcomingFSControlRules: DirectiveBatch[] = await this.directive batch service.find Upcoming By Start And Within Hours(now, hours); // splitting this since to avoid making the lock query too complex -// // const recentlyExecutedFSControlRules: DirectiveBatchExecution[] = await this.directive batch execution service.find By Created After(oneHourAgo); - -// // const upcomingGridIdSet = upcomingFSControlRules.map(({ grid }) => grid.id); -// // const recentlyExecutedGridIdSet = recentlyExecutedFSControlRules.map(({ directive_batch }) => directive_batch.grid.id); - -// // return uniq([ ...upcomingGridIdSet, ...recentlyExecutedGridIdSet ]); -// // } -// } diff --git "a/apps/tiamat/src/modules/\344\270\200meter-credit-transfers/meter-credit-transfers.module.ts" "b/apps/tiamat/src/modules/\344\270\200meter-credit-transfers/meter-credit-transfers.module.ts" deleted file mode 100644 index 436e67b..0000000 --- "a/apps/tiamat/src/modules/\344\270\200meter-credit-transfers/meter-credit-transfers.module.ts" +++ /dev/null @@ -1,12 +0,0 @@ -// import { Global, Module } from '@nestjs/common'; -// import { MeterCreditTransfersService } from './meter-credit-transfers.service'; -// import { MeterCreditTransfer } from '@core/modules/meter-credit-transfers/entities/meter-credit-transfer.entity'; -// import { TypeOrmModule } from '@nestjs/typeorm'; - -// @Global() -// @Module({ -// imports: [ TypeOrmModule.forFeature([ MeterCreditTransfer ]) ], -// providers: [ MeterCreditTransfersService ], -// exports: [ MeterCreditTransfersService ], -// }) -// export class MeterCreditTransfersModule {} diff --git "a/apps/tiamat/src/modules/\344\270\200meter-credit-transfers/meter-credit-transfers.service.ts" "b/apps/tiamat/src/modules/\344\270\200meter-credit-transfers/meter-credit-transfers.service.ts" deleted file mode 100644 index 272c94c..0000000 --- "a/apps/tiamat/src/modules/\344\270\200meter-credit-transfers/meter-credit-transfers.service.ts" +++ /dev/null @@ -1,257 +0,0 @@ -// import { HttpException, Inject, Injectable, forwardRef, OnModuleInit } from '@nestjs/common'; -// import { MeterCreditTransfersService as CoreMeterCreditTransfersService } from '@core/modules/meter-credit-transfers/meter-credit-transfers.service'; -// import { MeterCreditTransfer } from '@core/modules/meter-credit-transfers/entities/meter-credit-transfer.entity'; -// import { Repository } from 'typeorm'; -// import { InjectRepository } from '@nestjs/typeorm'; -// import { OrdersService } from '../orders/orders.service'; -// import { Meter } from '@core/modules/meters/entities/meter.entity'; -// import { MetersService } from '../meters/meters.service'; -// import { Account } from '@core/modules/accounts/entities/account.entity'; -// import { CreateDirectiveInput } from '@core/modules/directives/dto/create-directive.input'; -// import { CreateMeterCreditTransferInput } from '@core/modules/meter-credit-transfers/dto/create-meter-credit-transfer.input'; -// import { v4 as uuidv4 } from 'uuid'; -// import { RAW_QUERIES, LockNextCreditTransferPageResult, LockNextCreditTransferPageParams } from '@tiamat/queries'; -// // import { flatten } from 'ramda'; -// // import { UpdateMeterInput } from '@core/modules/meters/dto/update-meter.input'; -// import { UpdateMeterCreditTransferInput } from '@core/modules/meter-credit-transfers/dto/update-meter-credit-transfer.input'; -// // import { DirectiveRouterService } from '../directive-router/directive-router.service'; -// // import { CreateOrderDto } from '../orders/dto/create-order.dto'; - -// // There is no need for a run loop, like in directive and order services, -// // because we are going to leverage the directives and orders queues themselves. -// // Once a new meter credit transfer is initiated from the frontend we will -// // 1. Create the meter credit transfer object in db and mark is processing -// // 2. Create the corresponding order which will be picked up by the order queue as soon as it is in pending state -// // 3. Create the corresponding directives, which are going to be executed -// // sequentially once the order has been processed successfully. - -// @Injectable() -// export class MeterCreditTransfersService extends CoreMeterCreditTransfersService implements OnModuleInit { -// isMeterCreditProcessingRunning = false; - -// constructor( -// @InjectRepository(MeterCreditTransfer) -// protected readonly meterCreditTransferRepository: Repository, -// @Inject(forwardRef(() => MetersService)) -// protected readonly metersService: MetersService, -// @Inject(forwardRef(() => OrdersService)) -// protected readonly ordersService: OrdersService, -// // private readonly directiveRouterService: DirectiveRouterService, -// ) { -// super(meterCreditTransferRepository); -// } - -// onModuleInit() { -// // this.run(); -// } - -// createDirectives(meterCreditTransfer: MeterCreditTransfer): CreateDirectiveInput[] { -// const { sender_meter, receiver_meter } = meterCreditTransfer; - -// // initialised directives that are not going to be executed until unlocked by the system -// const directives: CreateDirectiveInput[] = [ -// { -// meter_id: sender_meter.id, -// directive_type: 'OFF', // switches off the sender meter -// directive_status: 'PENDING', -// directive_priority: 1500, -// meter_credit_transfer_id: meterCreditTransfer.id, -// }, -// { -// meter_id: sender_meter.id, -// directive_type: 'READ_CURRENT_CREDIT', //reads the current credit in the sender meter -// directive_status: 'INITIALISED', -// directive_priority: 1450, -// meter_credit_transfer_id: meterCreditTransfer.id, -// }, -// { -// meter_id: sender_meter.id, -// directive_type: 'CLEAR_CREDIT', //clears credit in the sender meter -// directive_status: 'INITIALISED', -// directive_priority: 1440, -// meter_credit_transfer_id: meterCreditTransfer.id, -// }, -// { -// // we do not set a topup amount since it can only be determined later on -// meter_id: sender_meter.id, -// directive_type: 'TOP_UP', //tops up the sender meter -// directive_status: 'INITIALISED', -// directive_priority: 1430, -// meter_credit_transfer_id: meterCreditTransfer.id, -// }, -// { -// // we do not set a topup amount since it can only be determined later on -// meter_id: receiver_meter.id, -// directive_type: 'TOP_UP', //tops up the receiver meter -// directive_status: 'INITIALISED', -// directive_priority: 1420, -// meter_credit_transfer_id: meterCreditTransfer.id, -// }, -// { -// meter_id: sender_meter.id, -// directive_type: 'ON', // switches the sender meter back on -// directive_status: 'INITIALISED', -// directive_priority: 1410, -// meter_credit_transfer_id: meterCreditTransfer.id, -// }, -// ]; - -// return directives; -// } - -// // It adds directives to the queue to be processed later by the run function -// async create(createMeterCreditTransferInput: CreateMeterCreditTransferInput, author: Account) { -// if (typeof createMeterCreditTransferInput.amount !== 'number') throw new HttpException('Amount in meter credit transfer should be a number', 500); - -// // check for sender meter existence -// const senderMeter: Meter = await this.metersService.findOne(createMeterCreditTransferInput.sender_meter_id); -// if (!senderMeter) throw new HttpException(`Sender meter with id ${ createMeterCreditTransferInput.sender_meter_id } not found`, 500); - -// // check for receiver meter existence -// const receiverMeter: Meter = await this.metersService.findOne(createMeterCreditTransferInput.receiver_meter_id); -// if(!receiverMeter) throw new HttpException(`Receiver meter with id ${ createMeterCreditTransferInput.receiver_meter_id } not found`, 500); - -// // generate a new object that's going to also have status and author properties -// const adjustedTransferForAccountAndState: CreateMeterCreditTransferInput = { -// ...createMeterCreditTransferInput, -// meter_credit_transfer_status: 'PENDING', -// author, -// }; - -// const meterCreditTransfer: MeterCreditTransfer = await this.meterCreditTransferRepository.save(adjustedTransferForAccountAndState); - -// // run the run loop, but do not wait for it to complete, since it running will -// // take several minutes -// this.run(); -// return meterCreditTransfer; -// } - -// // This is the main loop, processing all meter credit transfers -// async run() { -// if (![ 'production' ].includes(process.env.NXT_ENV)) return; - -// // if it's already running, then skip since busy -// if (this.isMeterCreditProcessingRunning) return; - -// try { -// this.isMeterCreditProcessingRunning = true; - -// const size = 100; -// let page = 0; -// let pendingTransfers: MeterCreditTransfer[]; - -// do { -// const uuid = uuidv4(); -// pendingTransfers = await this.lockNextMeterCreditTransfer(uuid, page++ * size, size); -// await this.processMeterCreditTransfers(pendingTransfers); -// } while(pendingTransfers.length); -// } -// catch (err) { -// console.error(err); -// } -// finally { -// // reopen semaphore so the next iteration can run again -// this.isMeterCreditProcessingRunning = false; -// } -// } - -// // It generates all directives and orders necessary for -// // the meter credit transfer to take place and implicitly triggers -// // the respective queues to process the changes -// async processMeterCreditTransfers(meterCreditTransfers: MeterCreditTransfer[]) { -// console.warn('Meter credit transfers not implemented', meterCreditTransfers); - -// // // for each meter credit transfer create directives and order -// // const meterCreditTransferEntities = meterCreditTransfers.map(creditTransfer => { -// // const { sender_meter, receiver_meter, amount, currency, author } = creditTransfer; - -// // const create_order_dto: CreateOrderDto = { -// // sender_wallet_id: sender_meter.wallet.id, -// // receiver_wallet_id: receiver_meter.wallet.id, -// // amount, -// // currency, -// // // We can hardcode it here, since it's always going to be done via Ayrton -// // payment_channel: 'AYRTON', -// // // The order is not yet set to PENDING, since we need the first directive to be completely processed before running it -// // order_status: 'INITIALISED', -// // author_id: author.id, -// // meter_credit_transfer_id: creditTransfer.id, -// // }; - -// // return { -// // id: creditTransfer.id, -// // create_order_dto, -// // update_meters: [ -// // { -// // id: sender_meter.id, is_manual_mode_on: true, -// // }, -// // { -// // id: receiver_meter.id, is_manual_mode_on: true, -// // }, -// // ], -// // create_directives: this.createDirectives(creditTransfer), -// // }; -// // }); - -// // // Create the orders -// // // @TODO :: Surely this can be done simpler -// // const createOrderDtos = meterCreditTransferEntities.map(({ create_order_dto }) => create_order_dto); -// // for (const createOrderDto of createOrderDtos) { -// // await this.ordersService.create(createOrderDto); -// // } - -// // // creates the directives and points them at the meter credit transfer -// // const directivesToCreate: CreateDirectiveInput[] = flatten(meterCreditTransferEntities.map(transfer => transfer.create_directives)); -// // await this.directiveRouterService.create(directivesToCreate); //this will trigger the directive loop, if it's not busy - -// // // make sure both meters are in manual mode, so FS control does not interfere with it -// // const updateMeterGroups: UpdateMeterInput[][] = meterCreditTransferEntities.map(transfer => transfer.update_meters); -// // const updateMeters: UpdateMeterInput[] = flatten(updateMeterGroups); -// // await this.metersService.updateMany(updateMeters); - -// // // Refetch updated objects that have been created -// // return this.findByIds(meterCreditTransfers.map(transfer => transfer.id)); -// } - -// async lockNextMeterCreditTransfer(lockSession: string, offset: number, limit: number) { -// // Run the query to lock the next set of meter credit transfers and -// // wait for it to complete, so the following query that fetches them -// // will be able to find them - -// const params: LockNextCreditTransferPageParams = [ -// lockSession, -// 'PROCESSING', -// 'PENDING', -// limit, -// offset, -// ]; -// const _result: LockNextCreditTransferPageResult = await this.meterCreditTransferRepository.query( -// RAW_QUERIES.sql.supabase.meterCreditTransfers.lockNextPage, -// params, -// ); - -// // Once we locked the page we are interested in, then we query its rows -// return this.findByLockSession(lockSession); -// } - -// // todo: find a better way of doing this -// async updateMany(updateMeterCreditTransferInput: UpdateMeterCreditTransferInput[]) { -// const toReturn: MeterCreditTransfer[] = []; -// for (const meterCreditTransfer of updateMeterCreditTransferInput) { -// const result: MeterCreditTransfer = await this.update(meterCreditTransfer.id, meterCreditTransfer); -// toReturn.push(result); -// } - -// return toReturn; -// } - -// async update(id: number, updateObject: UpdateMeterCreditTransferInput): Promise { -// await this.meterCreditTransferRepository.update(id, updateObject); -// return this.findOne(id); -// } - -// // not sure this is even needed -// remove(id: number) { -// return this.meterCreditTransferRepository.softDelete(id); -// } -// } diff --git a/apps/tiamat/src/queries/sql/supabase/meter-credit-transfers/lock-next-page.sql b/apps/tiamat/src/queries/sql/supabase/meter-credit-transfers/lock-next-page.sql deleted file mode 100644 index e0c5b82..0000000 --- a/apps/tiamat/src/queries/sql/supabase/meter-credit-transfers/lock-next-page.sql +++ /dev/null @@ -1,19 +0,0 @@ --- Lock a page of meter credit transfers for processing --- Selects pending transfers and assigns them to a lock session --- Used for batch processing of topups, purchases, and other credit operations - -WITH transfers_to_lock AS ( - -- Select a page of pending credit transfers - SELECT id - FROM meter_credit_transfers - WHERE meter_credit_transfer_status = $3 - LIMIT $4 - OFFSET $5 -) - --- Update selected transfers with lock session and new status -UPDATE meter_credit_transfers -SET - lock_session = $1, - meter_credit_transfer_status = $2 -WHERE id IN (SELECT id FROM transfers_to_lock); diff --git a/apps/tiamat/src/queries/sql/supabase/meter-credit-transfers/lock-next-page.types.ts b/apps/tiamat/src/queries/sql/supabase/meter-credit-transfers/lock-next-page.types.ts deleted file mode 100644 index e1b340a..0000000 --- a/apps/tiamat/src/queries/sql/supabase/meter-credit-transfers/lock-next-page.types.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MeterCreditTransferStatusEnum } from '@core/types/supabase-types'; - -/** - * Query parameters for lock-next-page.sql - */ -export type LockNextCreditTransferPageParams = [ - lockSession: string, - newStatus: MeterCreditTransferStatusEnum, - currentStatus: MeterCreditTransferStatusEnum, - limit: number, - offset: number, -]; - -/** - * Result type for lock-next-page.sql - * - * Locks a page of meter credit transfers for processing by assigning them to a lock session. - * Selects transfers in PENDING status and updates them to PROCESSING status. - * Used for batch processing of credit transfers (topups, purchases, etc.). - */ -export type LockNextCreditTransferPageResult = void; - diff --git a/apps/worker/.env.example b/apps/worker/.env.example new file mode 100644 index 0000000..493d419 --- /dev/null +++ b/apps/worker/.env.example @@ -0,0 +1,11 @@ +# Host-specific env for `worker`. +# Copy to `apps/worker/.env`. +# +# Shared secrets (`SUPABASE_URL`, `SUPABASE_SECRET_KEY`, LOG_*, NXT_CONFIG_*) live in +# the repo-root `.env` — see root `.env.example`. Nx merges: this file first, then root +# (first definition wins). +# +# Worker does not need publishable / JWKS auth secrets (api-only, Task 7). + +# --- Optional host port ----------------------------------------------------------- +# PORT=3000 diff --git a/apps/worker/.spec.swcrc b/apps/worker/.spec.swcrc new file mode 100644 index 0000000..3b52a53 --- /dev/null +++ b/apps/worker/.spec.swcrc @@ -0,0 +1,22 @@ +{ + "jsc": { + "target": "es2017", + "parser": { + "syntax": "typescript", + "decorators": true, + "dynamicImport": true + }, + "transform": { + "decoratorMetadata": true, + "legacyDecorator": true + }, + "keepClassNames": true, + "externalHelpers": true, + "loose": true + }, + "module": { + "type": "es6" + }, + "sourceMaps": true, + "exclude": [] +} diff --git a/apps/worker/eslint.config.mjs b/apps/worker/eslint.config.mjs new file mode 100644 index 0000000..724052a --- /dev/null +++ b/apps/worker/eslint.config.mjs @@ -0,0 +1,5 @@ +import baseConfig from "../../eslint.config.mjs"; + +export default [ + ...baseConfig +]; diff --git a/apps/worker/jest.config.cts b/apps/worker/jest.config.cts new file mode 100644 index 0000000..b8804a0 --- /dev/null +++ b/apps/worker/jest.config.cts @@ -0,0 +1,21 @@ +/* eslint-disable */ +const { readFileSync } = require('fs') + +// Reading the SWC compilation config for the spec files +const swcJestConfig = JSON.parse( + readFileSync(`${__dirname}/.spec.swcrc`, 'utf-8') +); + +// Disable .swcrc look-up by SWC core because we're passing in swcJestConfig ourselves +swcJestConfig.swcrc = false; + +module.exports = { + displayName: 'worker', + preset: '../../jest.preset.js', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': ['@swc/jest', swcJestConfig] + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: 'test-output/jest/coverage' +}; diff --git a/apps/worker/package.json b/apps/worker/package.json new file mode 100644 index 0000000..5534b6e --- /dev/null +++ b/apps/worker/package.json @@ -0,0 +1,108 @@ +{ + "name": "@nxt/worker", + "version": "0.0.1", + "license": "MPL-2.0", + "authors": [ + { + "name": "Bobby Bol", + "email": "studio.innua@gmail.com" + }, + { + "name": "Tommaso Girotto", + "email": "tommaso.girotto91@gmail.com" + } + ], + "private": true, + "nx": { + "name": "worker", + "targets": { + "build": { + "executor": "nx:run-commands", + "options": { + "command": "webpack-cli build", + "env": { + "NODE_ENV": "production" + }, + "cwd": "apps/worker" + }, + "configurations": { + "development": { + "env": { + "NODE_ENV": "development" + } + } + } + }, + "prune-lockfile": { + "dependsOn": [ + "build" + ], + "cache": true, + "executor": "@nx/js:prune-lockfile", + "outputs": [ + "{workspaceRoot}/apps/worker/dist/package.json", + "{workspaceRoot}/apps/worker/dist/pnpm-lock.yaml" + ], + "options": { + "buildTarget": "build" + } + }, + "copy-workspace-modules": { + "dependsOn": [ + "build" + ], + "cache": true, + "outputs": [ + "{workspaceRoot}/apps/worker/dist/workspace_modules" + ], + "executor": "@nx/js:copy-workspace-modules", + "options": { + "buildTarget": "build" + } + }, + "prune": { + "dependsOn": [ + "prune-lockfile", + "copy-workspace-modules" + ], + "executor": "nx:noop" + }, + "serve": { + "continuous": true, + "executor": "@nx/js:node", + "defaultConfiguration": "development", + "dependsOn": [ + "build" + ], + "options": { + "buildTarget": "worker:build", + "runBuildTargetDependencies": false + }, + "configurations": { + "development": { + "buildTarget": "worker:build:development" + }, + "production": { + "buildTarget": "worker:build:production" + } + } + }, + "test": { + "executor": "nx:noop" + } + } + }, + "dependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/core": "^11.0.0", + "@nestjs/platform-express": "^11.0.0", + "@nestjs/schedule": "^6.1.3", + "@nxt/core": "workspace:*", + "reflect-metadata": "^0.2.0", + "rxjs": "^7.8.0", + "tslib": "^2.3.0" + }, + "devDependencies": { + "@nestjs/testing": "^11.0.0" + } +} diff --git a/apps/worker/src/main.ts b/apps/worker/src/main.ts new file mode 100644 index 0000000..e478baa --- /dev/null +++ b/apps/worker/src/main.ts @@ -0,0 +1,19 @@ +import { NestFactory } from '@nestjs/core'; +import { Logger } from '@nestjs/common'; +import { loadConfig } from '@nxt/core/config'; + +async function bootstrap() { + // Config subpath has no Nest module side effects. loadConfig before NestFactory.create + // so forRootAsync factories / providers can call getConfig() (ADR-007 decision 3). + loadConfig(); + const { AppModule } = await import('./modules/app.module.js'); + + const app = await NestFactory.create(AppModule); + const logger = new Logger('Bootstrap'); + + const port = process.env.PORT || 3000; + await app.listen(port); + logger.log(`Application is running on: http://localhost:${ port }`); +} + +bootstrap(); diff --git a/apps/worker/src/modules/app.module.ts b/apps/worker/src/modules/app.module.ts new file mode 100644 index 0000000..f20b9a3 --- /dev/null +++ b/apps/worker/src/modules/app.module.ts @@ -0,0 +1,23 @@ +import { Module } from '@nestjs/common'; +import { ScheduleModule } from '@nestjs/schedule'; +import { + GlobalHttpModule, + GlobalLoggerModule, + GlobalSupabaseModule, +} from '@nxt/core'; + +import { HeartbeatModule } from './heartbeat/heartbeat.module'; + +/** Cross-cutting infra — Logger, Supabase, HTTP (3rd-party integrations). */ +const infrastructure = [ GlobalLoggerModule, GlobalSupabaseModule, GlobalHttpModule ]; + +/** Always-on Foundation for this host (scheduler + heartbeat). */ +const foundation = [ ScheduleModule.forRoot(), HeartbeatModule ]; + +// Tier-1 capability conditionals (empty until capabilities are imported). + +@Module({ + imports: [ ...infrastructure, ...foundation ], + controllers: [], +}) +export class AppModule {} diff --git a/apps/worker/src/modules/heartbeat/heartbeat.module.ts b/apps/worker/src/modules/heartbeat/heartbeat.module.ts new file mode 100644 index 0000000..6d212d9 --- /dev/null +++ b/apps/worker/src/modules/heartbeat/heartbeat.module.ts @@ -0,0 +1,8 @@ +import { Module } from '@nestjs/common'; + +import { HeartbeatService } from './heartbeat.service'; + +@Module({ + providers: [ HeartbeatService ], +}) +export class HeartbeatModule {} diff --git a/apps/worker/src/modules/heartbeat/heartbeat.service.ts b/apps/worker/src/modules/heartbeat/heartbeat.service.ts new file mode 100644 index 0000000..6d723b5 --- /dev/null +++ b/apps/worker/src/modules/heartbeat/heartbeat.service.ts @@ -0,0 +1,12 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { Interval } from '@nestjs/schedule'; + +@Injectable() +export class HeartbeatService { + private readonly logger = new Logger(HeartbeatService.name); + + @Interval(30_000) + handleHeartbeat(): void { + this.logger.log('heartbeat'); + } +} diff --git a/apps/worker/tsconfig.app.json b/apps/worker/tsconfig.app.json new file mode 100644 index 0000000..751825c --- /dev/null +++ b/apps/worker/tsconfig.app.json @@ -0,0 +1,31 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "types": [ + "node" + ], + "rootDir": "src", + "tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo", + "target": "es2021" + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "out-tsc", + "dist", + "jest.config.ts", + "jest.config.cts", + "src/**/*.spec.ts", + "src/**/*.test.ts", + "eslint.config.js", + "eslint.config.cjs", + "eslint.config.mjs" + ], + "references": [ + { + "path": "../../libs/core/tsconfig.lib.json" + } + ] +} diff --git a/apps/worker/tsconfig.json b/apps/worker/tsconfig.json new file mode 100644 index 0000000..63dbe35 --- /dev/null +++ b/apps/worker/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/apps/worker/tsconfig.spec.json b/apps/worker/tsconfig.spec.json new file mode 100644 index 0000000..2958eab --- /dev/null +++ b/apps/worker/tsconfig.spec.json @@ -0,0 +1,22 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./out-tsc/jest", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "jest.config.ts", + "jest.config.cts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts" + ], + "references": [ + { + "path": "./tsconfig.app.json" + } + ] +} diff --git a/apps/worker/webpack.config.js b/apps/worker/webpack.config.js new file mode 100644 index 0000000..44108b1 --- /dev/null +++ b/apps/worker/webpack.config.js @@ -0,0 +1,39 @@ +const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin'); +const nodeExternals = require('webpack-node-externals'); +const { join } = require('path'); + +module.exports = { + output: { + path: join(__dirname, 'dist'), + clean: true, + ...(process.env.NODE_ENV !== 'production' && { + devtoolModuleFilenameTemplate: '[absolute-resource-path]', + }), + }, + externals: [ + nodeExternals({ + modulesFromFile: { + fileName: join(__dirname, 'package.json'), + }, + }), + ], + plugins: [ + new NxAppWebpackPlugin({ + target: 'node', + compiler: 'tsc', + main: './src/main.ts', + tsConfig: './tsconfig.app.json', + assets: [ + // Bundled default config (ADR-007 decision 4) — ships next to main.js so a built + // image boots in evaluation mode with zero env. + { input: '../..', glob: 'config.default.json', output: '.' }, + ], + externalDependencies: 'none', + mergeExternals: true, + optimization: false, + outputHashing: 'none', + generatePackageJson: false, + sourceMap: true, + }), + ], +}; diff --git a/config.default.json b/config.default.json new file mode 100644 index 0000000..93a2d5c --- /dev/null +++ b/config.default.json @@ -0,0 +1,8 @@ +{ + "$schemaVersion": "1", + "public": { + "platformName": "NXT Grid Platform" + }, + "capabilities": {}, + "integrations": {} +} diff --git a/config.example.json b/config.example.json new file mode 100644 index 0000000..376a1fd --- /dev/null +++ b/config.example.json @@ -0,0 +1,8 @@ +{ + "$schemaVersion": "1", + "public": { + "platformName": "Acme Mini-Grid" + }, + "capabilities": {}, + "integrations": {} +} diff --git a/docs/architecture/004-open-source-architecture-and-capability-modularization.md b/docs/architecture/004-open-source-architecture-and-capability-modularization.md index c15589b..7b86a6e 100644 --- a/docs/architecture/004-open-source-architecture-and-capability-modularization.md +++ b/docs/architecture/004-open-source-architecture-and-capability-modularization.md @@ -58,7 +58,7 @@ onto **thin runtime hosts** (horizontal/operational). Capabilities expose **port - **Platform core (always on):** auth, api-keys, organizations, members, accounts, agents, grids, poles, dcus, routers, db/supabase, logging, websocket, download. - **(1) Energy Production Monitoring:** mppts + mppt/grid snapshots, victron, solcast & forecasts, - grid-diagnostics/digital-twin, device-data-sink, zerotier. + grid-diagnostics/digital-twin, zerotier. - **(2) Smart Metering & Distribution:** meters, meter-interactions/installs/commissionings, directive-batches (bulk meter-interactions), hardware imports/installs, connections, customers, device-messages, calin adapter, chirpstack/LoRaWAN, ussd-sessions. @@ -69,6 +69,32 @@ onto **thin runtime hosts** (horizontal/operational). Capabilities expose **port - **(5) Field Operations:** epicollect, issues, notes, jira, pd-flows/actions/hero. - **(6) Automation:** autopilot, lifeline (depends on (2) and/or (3)). +> **Amended 2026-07-13 (002b / register #12)** — **`device-data-sink` removed** from (1). The +> legacy generic device registry (`devices`, `device_types`, `device_logs`) and its Grafana-oriented +> read path are **out of OSS scope** — dropped from the canonical baseline schema, not a +> capability-flagged optional module. Production monitoring import (**002e**) must not resurrect +> those tables or the `meters.device_id` FK. Cutover spec: deviation register **#12**. + +> **Amended 2026-07-15 (002d — Foundation scope)** — the "platform core (always on)" list above is +> narrowed to what is genuinely always-on identity/access/infra. **Always-on Foundation** = +> `auth`, `api-keys`, `organizations`, `members`, `accounts`, `grids` (entity data + core CRUD), +> `db/supabase`, `logging`. The remaining items originally listed are **not** always-on and move +> out per **ADR-013** (behavior follows domain ownership): +> - `agents` — no standalone core module; the agent **entity/type** rides the accounts/identity +> graph, but every agent **behavior** (topups, management) is owned by Metering/Payments. +> - `dcus`, `poles` — metering-specific → **Metering** capability (poles: only a stale entity today). +> - `websocket` — realtime is a metering after-effect → **Metering** (first realtime emitter). +> - `routers` — unresolved; provisionally **Production Monitoring**, decided when that capability is +> imported. +> - `download` — deferred; not part of initial core. +> - Notification **core** (§4) remains platform in principle but its **import is deferred** to the +> first capability that writes notifications. +> +> `grids` is therefore an always-on entity with a **lean core surface only**; capability-specific +> grid behavior (e.g. connectivity stats) lives in the owning capability (**ADR-013**). Naming: the +> always-on layer is referred to as **Foundation** and is a per-host *selection*, not a library — +> see 002d. Import spec: `docs/plans/002-oss-migration/002d-platform-core-import.md`. + The primary independence boundary is **(1) Production vs (2)+(3) Metering/Payments**, over the shared platform core. Production is the clean island (depends only on platform core). @@ -127,17 +153,27 @@ a capability is off* (keep) vs *deprecated/historical-only* (exclude from baseli - Existing dual-ORM usage (legacy TypeORM + Supabase client on the primary DB) complicates module extraction and must be paid down alongside. -## Out of Scope / Deferred to Follow-up ADRs -- **ADR-005 — Inter-host communication:** shared-DB vs HTTP mesh vs internal event bus (today both a - shared DB and a bidirectional HTTP mesh are in use). -- **ADR-006 — Monorepo tooling & CI/CD:** Nx suitability / fresh setup, affected-only builds, remote - caching, per-host build & deploy, replacing the DigitalOcean-coupled stub workflow. -- **ADR-007 — Configuration & wiring mechanism:** config file format, conditional NestJS dynamic-module - loading per capability, boot-time validation of flags/providers. -- **ADR-008 — Open-source migration strategy:** re-scaffold + incremental module import; database - baseline/squash; deprecated-table (e.g. `directives` / `lorawan-directives`) phase-out; parity + cutover. -- **ADR-009 — Database migration deployment & governance:** operator-controlled (non-push-triggered) - migration application; keep migrations in the monorepo; separate-migrations-repo rejected. +## Follow-up ADRs + +Mechanism-level decisions that this ADR intentionally left to separate documents. Several are now +**Accepted** (summaries below); see each file for full status and any remaining deferred items. + +- **ADR-005 — Inter-host communication:** **Accepted (2026-07-17).** Independent hosts; shared DBs + carry state; residual sync HTTP (prefer worker→`api`); async via per-capability DB jobs; retire + the bidirectional `*_API` mesh; no broker as inter-host bus. See + `docs/architecture/005-inter-host-communication.md`. +- **ADR-006 — Monorepo tooling & CI/CD:** **Accepted.** Nx suitability / fresh setup, affected-only + builds, remote caching, per-host build & deploy, replacing the DigitalOcean-coupled stub workflow. +- **ADR-007 — Configuration & wiring mechanism:** **Accepted.** Config file format, conditional NestJS + dynamic-module loading per capability, boot-time validation of flags/providers. +- **ADR-008 — Open-source migration strategy:** **Accepted (strategy).** Re-scaffold + incremental + module import; database baseline/squash; deprecated-table (e.g. `directives` / `lorawan-directives`) + phase-out; parity + cutover. +- **ADR-009 — Database migration deployment & governance:** **Accepted.** Operator-controlled + (non-push-triggered) migration application; keep migrations in the monorepo; separate-migrations-repo + rejected. + +### Still out of scope (not decided here) - **Per-organization provider overrides** (generalizing ADR-003) as an optional payments feature. - **Dual-ORM consolidation** (TypeORM → Supabase client) as it interacts with capability extraction. @@ -145,3 +181,5 @@ a capability is off* (keep) vs *deprecated/historical-only* (exclude from baseli - A capability needs to differ per organization within one deployment (would reopen Decision 7). - A second real adapter appears for a port (validates/forces the Tier-2 abstraction; cf. ADR-001). - A capability's runtime profile diverges enough to warrant its own deployable (cf. Decision 8). +- The OSS baseline schema deviation register adds or removes objects that affect capability + boundaries (keep the capability map aligned — see Decision 5 amendment, register #12). diff --git a/docs/architecture/005-inter-host-communication.md b/docs/architecture/005-inter-host-communication.md index 63bf8b9..921d7cc 100644 --- a/docs/architecture/005-inter-host-communication.md +++ b/docs/architecture/005-inter-host-communication.md @@ -1,66 +1,225 @@ # ADR-005: Inter-Host Communication -**Date:** 2026-06-26 -**Status:** Open (deferred from ADR-004; to be resolved in a dedicated session) +**Date:** 2026-06-26 (decided 2026-07-17) +**Status:** Accepted — explicit prerequisite of authoring **002e** (Energy Production Monitoring). +002d (Foundation) completed 2026-07-17 without needing this lock. --- -## Purpose of this document +## Context -This is a **scoped stub** so the question can be picked up later in its own conversation with full -context. It records the problem, the current state, the options, and the constraints inherited from -ADR-004 — but does **not** yet make a decision. +ADR-004 defines a modular monolith: capability modules wired onto thin runtime hosts. The default +deployable footprint is **`api`** (HTTP-facing; tiamat + folded-in talos) and **`worker`** +(background/collector domains; config-driven composition along capability seams). A separate +deployable is justified only by a divergent runtime profile — never by code tidiness. -## Inherited context (from ADR-004) +The OSS migration roadmap (`docs/plans/002-oss-migration.md`) accepts this ADR as a prerequisite of +Production Monitoring import (**002e**): that is the first sub-plan that gives `worker` a real +capability and therefore real cross-host questions — i.e. the first import that applies this +accepted policy. Foundation (**002d**) wired Supabase on both hosts but carried no cross-host +capability traffic. -- Single-track open source, one monorepo, **modular monolith** with capability modules on thin - runtime hosts. -- Default hosts: **`api`** (tiamat + folded talos) and **`worker`** (background/collector domains), - with worker composition **config-driven along capability seams**. -- Decomposition principle: a separate deployable is justified **only by a divergent runtime profile**. -- Configuration is **per-deployment**. +### Legacy coupling (what we are leaving) -## The question +The four legacy apps were coupled **two ways at once**: -How should runtime hosts (and any future split-out services such as device-messaging) communicate — -efficiently and generically — especially when co-hosted on the same platform? +1. **Shared databases (dominant):** the same primary ops DB and the same Timescale instance — + effectively the integration bus. +2. **Bidirectional HTTP mesh (secondary):** `TALOS_API` / `YETI_API` / `LOCH_API` / `TIAMAT_API` + (+ API keys). Many of those edges were artifacts of the 4-app split (e.g. tiamat→talos) and + collapse to in-process calls once modules share a host. A smaller set are true residuals + (e.g. Epicollect sync calling `user-admin/create-customer`; collector jobs updating ops status + columns; notification send pipeline owning `notifications` rows). -## Current state (as observed) +Legacy also used Valkey/Redis heavily inside device-messaging queues, and Socket.IO for +client-facing realtime — neither is a general host↔host bus for the OSS baseline. -The apps are coupled **two ways at once**: +Company deploy today (DigitalOcean App Platform: `api` + *n* workers under one App, private VPC) +makes **private HTTP** a natural residual channel; it does not require a broker, and it does not +excuse missing app-level machine auth. + +### Constraints -1. **Shared databases (dominant coupling):** all apps connect to the *same* primary DB twice — - `NXT_DB_*` (legacy TypeORM) **and** `SUPABASE_*` (Supabase client) — plus the same - `NXT_TIMESCALE_DB_*`. The database is effectively the integration bus today. -2. **HTTP mesh (secondary):** `tiamat` holds `TALOS_API` / `YETI_API` / `LOCH_API`; `loch` and `yeti` - each hold `TIAMAT_API` + `TIAMAT_API_KEY`. A bidirectional, API-key-authenticated HTTP mesh sits on - top of the shared DB. +- **Self-hostability:** lean default footprint (`api` + `worker` + DBs); no mandatory broker for + inter-host communication (ADR-004). +- **Genericness:** mechanisms must not hardcode NXT-specific topology. +- **Explicit seams:** cross-host cost and availability must remain visible at call sites. +- **Capability boundaries:** respect ADR-004 / ADR-013 ownership; communication is not a way to + smuggle capability logic across the wrong module. +- **Flexibility with accountability:** strong defaults; exceptions allowed when justified and + **recorded** (this ADR’s exception bar, or the importing sub-plan decisions log) — not silent + mesh creep. -Note: `iovalkey` (a Valkey/Redis client) is already a dependency, and Socket.IO/WebSockets are in use. +## Decision -## Options to consider (not yet decided) +### 1. Hosts are independent by default; shared DBs carry state -1. **In-process calls when co-located.** Because of the modular monolith, many cross-host "calls" - collapse into direct service calls inside the same process once capabilities are wired onto a host. - Reduces the mesh to only genuinely cross-process interactions. -2. **Shared DB as the integration bus.** Lean on the database (and/or Postgres `LISTEN/NOTIFY`) for - coordination. Simple, no new infra, but couples hosts through schema and can hide implicit contracts. -3. **Internal event bus.** Introduce Valkey/Redis (already a dependency) or a lightweight broker for - pub/sub between hosts. Decouples producers/consumers; adds infra a self-hoster must run. -4. **Keep/standardize the HTTP mesh.** Formalize internal APIs with typed contracts and shared API - keys. Familiar, but adds network hops and an availability dependency between hosts. +`api` and `worker` are independent composition roots. Coordination is primarily through **shared +databases** (ops Supabase/`public`, Timescale, and any specialty stores a capability owns). +Residual host↔host calls are **allowed but exceptional** — the intent is maximum independence, not +a zero-RPC fantasy. The residual count need not be known up front; capability imports apply this +policy as edges appear. -## Constraints / evaluation criteria +**Client realtime** (Socket.IO, Supabase realtime to frontends) is **out of scope** for this ADR — +that is host→browser, not host→host. -- **Self-hostability:** every added piece of infra is a burden on the default operator; prefer the - smallest viable footprint (see ADR-004 lean-default goal). -- **Genericness:** the chosen mechanism must not hardcode NXT-specific topology. -- **Co-location efficiency:** avoid unnecessary network hops when hosts run on the same platform. -- **Capability boundaries:** communication should respect the capability seams from ADR-004. +### 2. Legitimate residual patterns -## To decide in the dedicated session +| Pattern | Meaning | Baseline mechanism | +|---|---|---| +| **Sync orchestration** | Caller needs another host’s business logic *and* an in-line result/error (e.g. Epicollect → create-customer) | Authenticated HTTP | +| **Async kick** | Caller wants work done later; does not need the outcome in the same request | Prefer **DB job / outbox** (per capability); HTTP only transitional/escape | +| **Shared-state dual access** | Both hosts read/write stores they are allowed to touch | Not “messaging” — just dual access under §7 | -- The default communication mechanism (and when it is allowed to differ). -- Whether to remove or formalize the existing HTTP mesh. -- Whether an event bus is justified for the OSS baseline or is an opt-in for larger deployments. -- How co-located vs distributed hosts are abstracted so code does not care which it is. +### 3. Sync orchestration = authenticated internal HTTP + +- **Reuse normal `api` routes** when they are a fit for machines (e.g. existing create-customer), + authenticated with machine credentials. +- Add a **narrow dedicated surface** (e.g. `/internal/...`) only when the public/human route is + wrong-shaped (authz, payload, side effects). Do not invent a second full API up front. +- Auth is **ADR-014** (`X-API-KEY` / `api_keys`, evolving scopes + route allowlist). No parallel + “internal shared secret” system. Private network / VPC is defense-in-depth, not a substitute. + +### 4. Async kicks prefer per-capability DB jobs + +- Prefer **per-capability / per-domain tables** (today’s `notifications` lifecycle is the + archetype; `pd_*` job state is similar). No mandatory global platform outbox in Foundation. +- Shared claim/retry **helpers** may appear when a second real user shows the same shape; do not + design a grand job bus for 002e. +- Fire-and-forget HTTP to enqueue work is a **transitional/escape** path only when no job table + exists yet for that flow. + +**Postgres `LISTEN`/`NOTIFY`** (or equivalent DB wake signals) is **not** a baseline mechanism. +Hosts poll/claim on their own schedule (cron/loops) or use HTTP for sync. Revisit later as an +optimization if a hot path demands it. + +### 5. Prefer unidirectional residual HTTP (workers → `api`) + +**Default:** in-stack residual HTTP is **worker (or other non-`api` host) → `api`**. Baseline +config gives those callers **`api`’s base URL + machine credential** — not a mesh of every host +URL. + +**`api` → worker HTTP** is allowed only when **all** of the following hold (or are explicitly +waived in the importing sub-plan), and the exception is **recorded**: + +1. Sync orchestration is truly required (in-line result), **and** +2. The work cannot live on `api` and cannot be a DB job the worker claims, **and** +3. The callee’s runtime profile justifies a separate host (ADR-004), **and** +4. The endpoint and rationale are written down (this ADR’s exceptions note or the sub-plan + decisions log). + +Operationally an exception is a **named** internal client + URL/env for that one surface — not a +return to a full bidirectional host matrix. + +### 6. No location-transparent RPC layer + +- **Same process (same host):** call Nest services in-process. Former HTTP between modules that now + share a host (e.g. talos folded into `api`) must not remain HTTP. +- **Cross-process:** use an **explicit** HTTP client or an explicit DB job write at the seam. +- Do **not** build a transport-switching port that hides whether a collaborator is local or remote. + Seams should look like seams. + +“Same toy box” means the same running process — not “merge `api` and `worker`.” Default topology +remains two hosts; modules that are worker-only by design may safely assume they dial `api`. + +### 7. Capability-owned ops DB writes (not “api sole writer”) + +Legacy already proved that “only tiamat writes ops” is tedious and routinely violated for good +reasons (telemetry status columns, notification send pipeline, field-ops job state). Baseline: + +- **Writers follow behavior ownership (ADR-013):** the host that runs a behavior’s writer may + write the tables/columns that behavior owns — including workers writing ops DB when appropriate. +- **HTTP → `api`** when the mutation needs shared orchestration or invariants (create-customer, + payouts, issue recalculation, etc.). +- Workers may write, without apology: + - **Job / outbox / pipeline state** they own (e.g. `notifications` status), + - **Narrow telemetry / denormalized status** that is a side-effect of collection (e.g. DCU + online flags, meter last-consumption timestamps, production-owned `grids` monitor fields), + - **Specialty stores** they own (e.g. Timescale for Production Monitoring). +- This is **not** “any host may write anything.” Random cross-cutting writes of another + capability’s orchestration path remain forbidden; use HTTP or move the behavior. + +### 8. Retire the bidirectional `*_API` mesh + +The legacy env matrix (`TALOS_API` / `YETI_API` / `LOCH_API` / `TIAMAT_API` + keys) is **migration +debt**, not a pattern to formalize. As each capability is imported: + +- Delete obsolete host-to-host URLs. +- Keep **`api` base URL + machine credential** on in-stack callers that need sync orchestration. +- Add **named reverse URLs** only for recorded §5 exceptions. +- Integrable extracted services (§9) use their own client config (service URL + callback + registration), not the old mesh variable names. + +### 9. `worker` is not an HTTP peer server by default + +Default `worker` shape: **cron / consumer** — outbound HTTP to `api` when needed; inbound HTTP +limited to **health/probes** (and whatever the deploy platform requires). + +Narrow inbound HTTP on a worker is allowed as a **recorded exception**, especially when a +particular worker composition needs it. Time and composition will force some exceptions; the +default remains “not a peer API server.” + +### 10. No broker as the inter-host integration bus + +Valkey/Redis (or any message broker) is **not required** for `api`↔`worker` communication in the +OSS baseline. Async prefers DB jobs; sync uses HTTP. + +A broker may still appear: + +- as **private implementation** inside an extracted service (device-messaging in-flight state — + ADR-010), or +- as an **opt-in** for large deployments later, + +but ADR-005 does not make a broker the platform mesh. + +### 11. Integrable extracted services (device-messaging) are not in-stack peers + +**In-stack hosts** (`api` ↔ `worker`): same household — swap phone numbers under §5–§6; fridge +notes (DB) are normal. + +**Integrable extracted services** (device-messaging per ADR-010, and similar future split-outs): +more like a delivery company any adopter can hire: + +- Call **their** HTTP API to enqueue/inspect/cancel work. +- **Register callback URL(s)** up front (or equivalently configure webhooks) so they know where + to deliver updates — the callback need not be on every request. +- Their Redis/Valkey (or other volatile store) is **private** to that service. +- Interactions with third-party platform components (Chirpstack, STS token generator, …) are that + service’s outbound integrations, not a second nxt host mesh. + +Endpoint and webhook details remain owned by **ADR-010**. This ADR only classifies the +relationship so imports do not pretend device-messaging is “just another worker.” + +## Consequences + +### Positive + +- Lean self-host default: `api` + `worker` + DBs; no mandatory broker or location-transparent RPC. +- Most legacy mesh edges disappear (same-host in-process) or become DB dual-access / job tables. +- Residual sync paths stay simple (HTTP + ADR-014) and fit DO VPC and docker-compose private + networks alike. +- Write rules match production reality (telemetry + job pipelines) without reintroducing + “everything must finish via api.” +- Device-messaging stays adoptable outside the nxt stack while sharing one cross-host *policy* + family (HTTP + callbacks), not a special platform bus. + +### Negative / Risks + +- Capability imports must apply judgment: HTTP vs DB job vs direct write — wrong choice recreates + mesh tedium or hidden coupling. Mitigated by this ADR’s tables and the migration decisions log. +- Unidirectional HTTP default will need recorded exceptions; discipline required so exceptions do + not become the mesh again. +- Per-capability job tables may duplicate claim/retry patterns until a second user justifies shared + helpers. +- Direct worker writes to ops columns on shared entities need clear ownership (ADR-013) so + “status side-effect” does not become unbounded schema coupling. + +## Related + +- **ADR-004** — hosts, decomposition principle, lean default footprint, capability map. +- **ADR-007** — per-deployment config; where `api` base URL / machine credentials are supplied. +- **ADR-010** — device-messaging extraction; HTTP API + webhook callbacks; private Redis. +- **ADR-012** — cutover notes `api`/`worker` flip asymmetry; database as primary integration point. +- **ADR-013** — capability-owned behavior over shared entities (write/ownership companion). +- **ADR-014** — machine credentials, scopes, route allowlist for residual HTTP. +- **002e** — Energy Production Monitoring import; first major consumer of this ADR. diff --git a/docs/architecture/006-monorepo-tooling-and-cicd.md b/docs/architecture/006-monorepo-tooling-and-cicd.md index 3a6e2df..5ca28a1 100644 --- a/docs/architecture/006-monorepo-tooling-and-cicd.md +++ b/docs/architecture/006-monorepo-tooling-and-cicd.md @@ -40,7 +40,7 @@ are dropped, not ported. Where a real version pin is still needed, it is express types → typecheck 120+ consumers atomically" driver that motivated the monorepo (ADR-004 decision 2). Turborepo and bare npm workspaces offer no equivalent affected-graph awareness for this use case. -**Fresh scaffold at Nx 23.0.1 (latest stable) with the full-modern layout:** +**Fresh scaffold at Nx 23.0.2 (latest stable) with the full-modern layout:** - **Project Crystal / inferred targets** (`@nx/webpack/plugin`, `@nx/jest/plugin`, `@nx/eslint/plugin` in `nx.json`). Per-project `project.json` files collapse to name + tags + @@ -54,7 +54,7 @@ Turborepo and bare npm workspaces offer no equivalent affected-graph awareness f project's `package.json`. **Achieved via re-scaffold (not `nx migrate`).** A new `create-nx-workspace` workspace is created -at 23.0.1 and domain code is moved in per the ADR-008 Phase 3 process, preserving git history +at 23.0.2 and domain code is moved in per the ADR-008 Phase 3 process, preserving git history where possible. ### 2. Package manager: pnpm (Corepack-pinned) @@ -79,7 +79,7 @@ are non-negotiable for cache correctness, type-drift guard accuracy, and Docker ### 3. Build executor: Nx NestJS template defaults -The `@nx/nest:application` generator at Nx 23.0.1 is accepted as-is: +The `@nx/nest:application` generator at Nx 23.0.2 is accepted as-is: - **Nest apps → `@nx/webpack` (`compiler: tsc`).** The Nx Nest generator hardcodes webpack with the comment "Some features require webpack plugins such as TS transformers." This is correct: NestJS @@ -152,7 +152,7 @@ not a build target. No new Nx project is needed per logical worker. **Local development.** `nx serve api` and `nx serve worker` are the development commands — identical in feel to `nx serve tiamat` today. Chromium/Docker is not required for the inner loop; only `supabase start` (local Postgres) is needed. For the two-logical-workers scenario locally: -`nx build worker --watch` in one terminal; `node dist/apps/worker/main.js` run twice with different +`nx build worker --watch` in one terminal; `node apps/worker/dist/main.js` run twice with different `NXT_CONFIG=` values. ### 7. Dockerfile (for future image-based deployment) @@ -166,10 +166,20 @@ ARG APP=api # api | worker Stages: 1. **base** — `node:24-slim` + Corepack + pnpm. -2. **build** — `pnpm install --frozen-lockfile` (BuildKit cache mount on the pnpm store) → - `nx build ${APP}`. -3. **runtime** — `pnpm deploy --prod --filter=@nxt/${APP}` for a pruned `node_modules`, copy - `dist/apps/${APP}`, run as a **non-root user** (`node`). +2. **build** — manifest-first `pnpm install --frozen-lockfile` (BuildKit cache mount on the pnpm + store) → `nx sync` → `nx run ${APP}:prune` (build + pruned lockfile + `workspace_modules/`). +3. **runtime** — copy `apps/${APP}/dist` (webpack output + pruned manifests); `pnpm install --prod + --frozen-lockfile --config.node-linker=hoisted` for prod `node_modules`; run as a **non-root + user** (`node`) via `node main.js`. + +**Runtime pruning: Nx prune, not `pnpm deploy`.** The original sketch used `pnpm deploy --prod +--filter=@nxt/${APP}`. pnpm 11 requires `inject-workspace-packages=true` or `--legacy` for deploy +in a `workspace:*` monorepo; the scaffold uses the Nx prune targets already wired per app +(`prune-lockfile`, `copy-workspace-modules`) instead. Hoisted linker at runtime is required so +`workspace_modules/@nxt/core` resolves its own prod deps (e.g. `zod`) under Node ESM. + +**Output path:** `@nx/webpack` emits to `apps/${APP}/dist/` (not the legacy `dist/apps/${APP}/` +layout). Base image rationale: `node:24-slim` (Debian slim) over Alpine — glibc avoids native-module incompatibilities (`pg`, `mongodb`, `mqtt`). Distroless is a future hardening option; it complicates @@ -183,15 +193,15 @@ image), never part of the lean `api`/`worker` defaults. ### 8. Deployment baseline: DigitalOcean from a GitHub branch **Simple baseline: DO App Platform deploys directly from a GitHub branch.** DO builds the app using -either the buildpack (build command + run command) or a Dockerfile-in-repo. The specific build method -is left open and will be determined when the fresh scaffold lands. +either the buildpack (build command + run command) or the root `Dockerfile` (parameterized via +`APP=api|worker`). Both paths are valid; Dockerfile-in-repo is verified (002c Task 6). -Expected DO component configuration: +Expected DO component configuration (buildpack — full workspace checkout, larger artifact): | Component | Build command | Run command | |-----------|---------------|-------------| -| `api` | `pnpm install && nx build api` | `node dist/apps/api/main.js` | -| `worker` | `pnpm install && nx build worker` | `node dist/apps/worker/main.js` | +| `api` | `corepack enable && pnpm install --frozen-lockfile && nx sync && nx build api` | `node apps/api/dist/main.js` | +| `worker` | `corepack enable && pnpm install --frozen-lockfile && nx sync && nx build worker` | `node apps/worker/dist/main.js` | **Known tradeoff.** DO's branch-based builder has no Nx-graph awareness; it rebuilds broadly on every push. The `affected` benefit from decision 5 applies to **CI validation only** (PR checks), @@ -282,7 +292,7 @@ repo's `.github/workflows/` when implemented. ### 11. Lingering `nx` / high-CPU issue The known issue of a lingering `nx` process consuming high CPU after `serve` is treated as -**resolved by the fresh Nx 23.0.1 scaffold**. The workaround note in `README.md` and the +**resolved by the fresh Nx 23.0.2 scaffold**. The workaround note in `README.md` and the `fix-node-cpu` script (`npm rebuild fsevents`) are removed. If the issue reappears on a specific platform, it is filed as a bug against the scaffold — not documented as an expected quirk. diff --git a/docs/architecture/007-configuration-and-wiring-mechanism.md b/docs/architecture/007-configuration-and-wiring-mechanism.md index e24dda4..8303fa2 100644 --- a/docs/architecture/007-configuration-and-wiring-mechanism.md +++ b/docs/architecture/007-configuration-and-wiring-mechanism.md @@ -1,7 +1,12 @@ # ADR-007: Configuration & Wiring Mechanism **Date:** 2026-06-30 -**Status:** Accepted (mechanism decided; several refinements deliberately deferred — see "Deferred / future") +**Status:** Accepted (mechanism decided; several refinements deliberately deferred — see "Deferred / +future"). **Amended 2026-07-09** — admin organization becomes DB-native; see "Amendment (2026-07-09)" +below (supersedes part of decisions 1 and 10). **Amended 2026-07-15 (002d)** — the `deployment` +config group is dropped, the wiring mechanism becomes explicit central per-host composition, and the +boot model requires DB for Foundation-wired hosts; see "Amendment (2026-07-15)" below (supersedes +decisions 7 and 10, and the `deployment` subtree of decision 5). --- @@ -60,6 +65,11 @@ The admin organization is a **config-supplied id** (category B), **not** a new D deployment-wide singleton that must be known before DB-dependent code initializes, and a config value is trivially overridable per environment without a data migration. +> **Amended 2026-07-09** — this classification is superseded for the admin organization specifically: +> it is now DB-native (`organizations.organization_type = 'PLATFORM_OPERATOR'`). See "Amendment +> (2026-07-09)" below. The rest of decision 1 (topology/presentation categories, secrets-in-env) is +> unaffected. + ### 2. Canonical format = JSON; contract = Zod; no `@nestjs/config` - The canonical artifact is **plain JSON** — exactly what a future CMS dump emits natively, so there is @@ -183,6 +193,11 @@ CMS dump silently mis-wiring a deployment). - All call sites (both auth strategies, `epicollect.controller.ts`, `payouts.service.ts`) read via `getConfig().deployment.*` — a near-mechanical find-and-replace, not a constructor refactor. +> **Amended 2026-07-09** — `adminOrganizationId`'s *source of truth* moves to the database (see +> Amendment below); `systemWalletId` is unaffected and still follows this decision as originally +> written. How backend/frontend call sites resolve the now-DB-native admin organization is **not yet +> decided** — see Amendment "Open / deferred." + ### 11. Frontend/backend shared artifact - The **distribution mechanism to the frontend is deferred** (it belongs with the CMS-era work). @@ -192,6 +207,149 @@ CMS dump silently mis-wiring a deployment). --- +## Amendment (2026-07-09) — admin organization becomes DB-native + +**Context:** surfaced during 002b Task 3c (schema programmability review). Postgres RLS +(`rls_check_if_nxt_member()`, hard-coded `nxt_org_id := 2`) cannot call `getConfig()` — the admin +organization must be resolvable **inside the database**, and RLS is on the hot path, so the resolution +must be fast (no per-row DB lookup). + +**Decision:** the single source of truth for "which organization is the platform operator" moves from +the config artifact (`deployment.adminOrganizationId`, decisions 1/10 above) to the database itself: + +- `organization_type_enum` gains a new value: **`PLATFORM_OPERATOR`**. +- A partial unique index enforces **at most one** organization with that type: + ```sql + CREATE UNIQUE INDEX one_platform_operator_org + ON organizations (organization_type) + WHERE organization_type = 'PLATFORM_OPERATOR'; + ``` +- A trigger on `organizations` (`AFTER INSERT OR UPDATE OF organization_type` and `AFTER DELETE`) keeps + a fast-read cache in sync: a Postgres **custom GUC** (`app.admin_organization_id`), set via both + `SET` (immediate effect for the current session) and `ALTER DATABASE … SET` (durable — applied to + future connections) whenever the flag changes. +- RLS reads the GUC via `current_setting('app.admin_organization_id', true)` — an in-memory, + zero-I/O read — instead of a live `organizations` lookup (index + heap fetch + MVCC check on every + evaluation). The renamed function (`rls_check_if_admin_org_member()`, replacing + `rls_check_if_nxt_member()`) is also marked `STABLE` so Postgres evaluates it once per statement, + not once per row. +- Setup flow: create the platform-operator organization (Supabase UI or SQL), set its + `organization_type = 'PLATFORM_OPERATOR'`; the trigger records its id automatically. No manual script + required for the common path (a script remains a fine manual fallback/override). + +**Rationale:** the value is a fact about which row in `organizations` is special, not a boot-time +topology decision — none of its current call sites (`api-key.strategy.ts`, `supabase.strategy.ts`, +`payouts.service.ts`) gate module composition or run before the DB is available, so decision 1's +original justification ("must be known before DB-dependent code initializes") does not hold for this +specific value. Flipping the flag is also friendlier to operate (an `UPDATE` + trigger) than editing +and redeploying the config artifact. `PLATFORM_OPERATOR` (not `SUPERADMIN`) avoids colliding with +`member_type_enum.SUPERADMIN`, which is an unrelated concept (a **member's** role within an org). + +**Superseded:** decision 1's classification of the admin organization as "category B, config-supplied, +not a DB column/flag" (now DB-native for this value only). Decision 10's plan to have all consumers +read `getConfig().deployment.adminOrganizationId` populated from the JSON artifact is superseded for +the *source of truth*; how each consumer resolves the value is **open** (below). `systemWalletId` is +unaffected by this amendment. + +**Open / deferred (do not lose these):** + +- **Backend consumers:** keep the `getConfig().deployment.adminOrganizationId` call sites, but populate + that field from the DB (query/cache at boot) instead of the JSON artifact — or have consumers query + `organizations` directly (cached, not per-request). Not decided. +- **Frontend consumers:** qilin/pegasus/eos/niffler/sphinx may need the same fact (e.g. + `is_nxt_grid_member`-style checks). If the DB is the source of truth, frontend delivery needs its own + resolution path (API endpoint, or republish into the shared config artifact from the DB at + build/boot time). **Not decided** — revisit alongside the frontend config delivery decision already + deferred in decision 11. +- **GUC propagation lag:** `ALTER DATABASE … SET` only takes effect for *new* connections; already-open + pooled connections keep the old value until they reconnect. Acceptable for a value that changes at + most once per deployment lifetime; document for operators. +- **Trigger function privilege:** needs `SECURITY DEFINER`, owned by a role with privilege to + `ALTER DATABASE` (typically `postgres`, which owns the database in Supabase-hosted projects) — + confirm this holds for self-hosted/vanilla Postgres adopters at Task 5/8. + +**Recorded in:** `docs/plans/002-oss-migration/002b-schema-programmability-review.md` (H1c) and +`002b-schema-deviation-register.md` (register #22, Programmability adjustments) — 002b Task 3c. + +> **Amended 2026-07-13 (002b Task 8)** — the GUC + `sync_admin_organization_id_guc` trigger +> mechanism above is **superseded in the shipped baseline**. Supabase runs migrations as +> non-superuser `postgres`, which cannot `ALTER DATABASE … SET` custom parameters (bootstrap +> failed with `42501`); assigning the trigger function to `supabase_admin` in-migration also +> fails (`must be able to SET ROLE "supabase_admin"`). **Shipped implementation:** drop the GUC +> trigger; `rls_check_if_admin_org_member()` reads the `PLATFORM_OPERATOR` row directly +> (`LANGUAGE sql STABLE`, `one_platform_operator_org` index). Enum value + partial unique index +> unchanged. Post-migration bootstrap: `docs/deployment/supabase.md` §5. Register #22 + +> Programmability adjustments §3 updated. The "GUC propagation lag" and "Trigger function +> privilege" open items below are **closed** by this amendment. + +--- + +## Amendment (2026-07-15) — 002d Foundation import: wiring, `deployment` drop, boot model + +Surfaced while planning the platform-core import +(`docs/plans/002-oss-migration/002d-platform-core-import.md`). Three related changes. + +### A. Wiring is explicit central per-host composition (supersedes decision 7) + +Decision 7's per-capability **contribution functions** (`xModules(config)`) are **not adopted**. +The temporary `demoModules(getConfig())` scaffold from 002c is their last vestige and is removed in +002d. Instead: + +- Each host composes its own module list **explicitly and centrally** in its `app.module.ts` using + plain named arrays — an `infrastructure` array and a `foundation` array — plus **inline Tier-1 + conditionals** for capabilities (`...(cfg.capabilities.metering?.enabled ? [MeteringModule] : [])`). +- There is **no shared "always-on" constant** across hosts: `api` and `worker` each list their own + infrastructure/foundation modules, so each host's composition is readable in one place. (`api`'s + Foundation is large; `worker`'s is small.) +- **Per-capability / per-adapter fail-fast stays where decision 9 (Layer 2) put it** — inside the + module/adapter's own wiring (`forRoot`/constructor via `requireEnv`), not in a wrapper function. +- **Extract trigger:** keep composition inline in `app.module.ts` until it gets crowded (~20+ + imports, or when Tier-2 `forRoot` wiring starts obscuring the Tier-1 booleans), then move it to a + co-located `app.composition.ts`. Not a lib. + +The **central capability registry** (Deferred / future) remains the eventual home for auto-derived +host composition and the effective-config report; this amendment does not build it, and the explicit +arrays refactor into it cleanly if/when it lands. + +### B. The `deployment` config group is dropped (supersedes decision 10; closes the 07-09 open item) + +The entire `deployment` subtree (`adminOrganizationId`, `systemWalletId`) is **removed from the +config schema** (decision 5's shape and decision 10 no longer apply). Rationale: every consumer of +both values lives in a **deferred capability**, so nothing in the Foundation reads either one, and +keeping empty config surface invites premature coupling. + +- **`adminOrganizationId`** — already DB-native since the 2026-07-09 amendment + (`organizations.organization_type = 'PLATFORM_OPERATOR'`). Its **backend consumers** (the + `payouts.service.ts` / `epicollect` gate) resolve it **DB-side within their owning capability** + (Payments / Field Ops) when those capabilities are imported — **not** via a config field. This + **closes** the 2026-07-09 "Open / deferred → Backend consumers" item (the answer is: no + config-populated field; resolve DB-side per consumer). Foundation auth does **not** compute an + admin-org membership flag in 002d (no in-scope reader); when reintroduced it is named + `is_admin_org_member` (was `is_nxt_grid_member`). +- **`systemWalletId`** — returns as **`bankingSystemWalletId`** under `capabilities.payments` + (owned by the Payments capability), **not** a resurrected top-level `deployment` group, when + Payments is imported. +- **Frontend resolution** of the admin-org fact remains open (unchanged) — see the 2026-07-09 + Amendment "Open / deferred → Frontend consumers" and decision 11. + +### C. Boot model — Foundation-wired hosts require the database (refines decision 4 "evaluation mode") + +Once a host wires Foundation/Supabase infrastructure, it **requires DB connectivity and fails fast +on missing `SUPABASE_*` env** at boot (`requireEnv` in the Supabase provider). "Evaluation mode" +(decision 4 — "a bare clone runs") is clarified to mean **capabilities off + a local Supabase**, not +**DB-less**. Both `api` and `worker` wire Supabase infra in 002d, so both require DB env from then +on. Per-host env differs: admin-client vars (`SUPABASE_URL`, `SUPABASE_SECRET_KEY`) on +both; `SUPABASE_PUBLISHABLE_KEY` + JWT verification (`SUPABASE_JWKS_URL` preferred; +`SUPABASE_JWT_SECRET` legacy fallback) only where auth runs (`api`). `SupabaseService` +builds its client in a **provider** (no import-time `export const supabase` singleton); the +query-type-generation shortcut returns later as a **type-only probe** (no runtime client). + +**Superseded by this amendment:** decision 7 (contribution functions) in full; decision 10 and the +`deployment` subtree of decision 5's schema shape; the 2026-07-09 "Backend consumers" open item +(now resolved as DB-side-per-consumer). + +--- + ## Consequences ### Positive @@ -257,3 +415,9 @@ CMS dump silently mis-wiring a deployment). - The effective-config report or automated host composition becomes needed (build the central registry). - A port gains a real manual-mode fallback (introduce the null/manual adapter + `whenUnprovided` policy). - The CMS integration lands (formalize generated JSON Schema, URL/build-time delivery, and FE distribution). +- The frontend config delivery mechanism is designed (resolve how the now-DB-native + `adminOrganizationId` reaches frontend apps — see Amendment "Open / deferred"). +- ~~Backend consumers of `getConfig().deployment.adminOrganizationId` are touched (002b Task 5/8) — + decide DB-populated-config vs. direct-DB-query at that point.~~ **Resolved 2026-07-15 (002d):** the + `deployment` group is dropped; backend consumers resolve the admin org **DB-side within their + owning capability**. See "Amendment (2026-07-15) → B". diff --git a/docs/architecture/011-group-targeted-load-shedding-commands.md b/docs/architecture/011-group-targeted-load-shedding-commands.md new file mode 100644 index 0000000..373b10e --- /dev/null +++ b/docs/architecture/011-group-targeted-load-shedding-commands.md @@ -0,0 +1,269 @@ +# ADR-011: Group-Targeted Load-Shedding Commands + +**Date:** 2026-07-09 +**Status:** Proposed +**Related:** [ADR-002](./002-crossroads-meter-state-management.md), [ADR-002a](./002a-reconciliation-controller-pattern.md) + +--- + +## TL;DR + +"FS control" today is a hardcoded binary concept: `meter_command_batches.fs_command` (`ON`/`OFF`) is a +second, parallel "purpose" column alongside `command_type` (formerly `task_type`), and execution +branches at runtime on `grid.uses_dual_meter_setup` to decide whether "FS off" means cutting a relay +(`TURN_ON`/`TURN_OFF`) or throttling a shared meter to zero (`SET_POWER_LIMIT`). This conflates four +things that should be independent: *which command* runs, *what value* it carries, *which meters* it +targets, and *how a grid is wired*. + +**Decision:** collapse `fs_command` into `command_type` (one column, typed as the existing +`meter_interaction_type_enum`), replace the implicit ON/OFF-to-value translation with an explicit, +generic `command_payload` (JSONB, static-or-resolved), and replace the implicit +`uses_dual_meter_setup` branching with an explicit `target_meter_type` filter. Full flexible +meter-grouping (by tariff, connection type, location, …) is intentionally deferred — this ADR +establishes the mechanism it will plug into without committing to *how* group membership is computed. + +This is a pure generalization: "turn FS on" becomes "`SET_POWER_LIMIT`, value `0`, target `FS`" — no +functionality is lost, and the removal of the `uses_dual_meter_setup` runtime dependency is what +makes that column's already-confirmed drop (OSS baseline, schema deviation register #17/§7) actually +safe to execute. + +--- + +## Context + +### What exists today + +`meter_command_batches` (currently named `directive_batches` in the legacy schema; already +confirmed renamed to `meter_command_batches`/`meter_command_batch_executions` as part of the OSS +migration baseline, schema deviation register #16) drives scheduled, grid-wide meter operations. +It has two mutually-exclusive "purpose" columns: + +- `task_type: meter_interaction_type_enum` — scan/read operations (`READ_VOLTAGE`, `READ_POWER`, …). +- `fs_command: fs_command_type_enum` (`'ON' | 'OFF'`) — the load-shedding toggle. + +`DirectiveBatchService.executeRule` (`apps/tiamat/src/modules/directive-batches/directive-batches.service.ts`) +branches on which column is set: + +```ts +if (rule.fs_command) { + meterInteractionType = isDualMeterGrid + ? (rule.fs_command === 'ON' ? 'TURN_ON' : 'TURN_OFF') + : 'SET_POWER_LIMIT'; +} +if (rule.task_type) meterInteractionType = rule.task_type; +``` + +For `SET_POWER_LIMIT`, the target value is derived, not stored: `0` for `ON`, or +`meter.power_limit_hps_mode` (each meter's own configured limit) for `OFF`. Meter targeting is +similarly implicit: `is_manual_mode_on = false` is applied only for `fs_command` batches (not for +scans), and dual-meter grids additionally filter to `meter_type = 'FS'`. + +Intent is tracked redundantly at grid level (`grids.should_fs_be_on`, `is_fs_on`) and meter level +(`meters.should_be_on` for relay control, `meters.power_limit_should_be` for throttling) — the +meter-level fields are the durable "intent" columns per ADR-002a's intent/reality model; the +grid-level fields are a cache that only ever made sense for a whole-grid binary toggle. + +Two other subsystems key off `fs_command` / the grid-level fields directly: qilin's +`FsControlPredictor`/`FsControlSlider` (a dual-time-slider UI that writes `fs_command` rows), and +`AutopilotService`, which reads `grid.is_fs_on` as forecast input. Neither is touched by this ADR +(see "Out of scope"). + +### Why this needs to change + +The operational concept has outgrown the schema. Load-shedding is no longer just "grid-wide FS +on/off" — it needs to express "set this group of meters to this power limit" for arbitrary groups +and arbitrary values, not just a global binary toggle at `0` or "restore to HPS limit". Continuing +to bolt cases onto `fs_command`/`executeRule`'s conditionals would compound the exact problem this +ADR removes: business logic encoded as runtime branching on implicit facts (`uses_dual_meter_setup`, +which command column happens to be set) instead of explicit, validated configuration. + +`grids.uses_dual_meter_setup` is already confirmed **dropped** in the OSS baseline (schema deviation +register #17/§7, "unused in OSS baseline") — but it is not actually unused yet; `executeRule` reads +it today. This ADR is a prerequisite for that drop being safe, not just a documentation +tidy-up. + +--- + +## Decisions + +### 1. Merge `fs_command` into `command_type` + +`fs_command` is dropped. The surviving column keeps the name `command_type` (renamed from +`task_type` to match the parent table's `meter_command_batches` rename) and is typed as +`meter_interaction_type_enum` directly — the same enum `meter_interactions.meter_interaction_type` +already uses. No new, narrower enum is introduced: any interaction type is technically batchable, +even though the batch-creation UI will offer a curated subset (`TURN_ON`, `TURN_OFF`, +`SET_POWER_LIMIT`, plus the existing read types). This removes the two-column "which field is set" +branching in `executeRule` entirely — the command a batch runs is simply `command_type`, no +translation step. + +### 2. Generic `command_payload` for command parameters + +A new nullable `command_payload: jsonb` column carries whatever parameter a `command_type` needs, +as a small discriminated union: + +```ts +type StaticPayload = { mode: 'STATIC'; value: T }; +type ResolvedPayload = { mode: 'RESOLVED'; resolver: string }; +type CommandPayload = StaticPayload | ResolvedPayload; +``` + +- Parameter-less commands (`TURN_ON`, `TURN_OFF`, `READ_VOLTAGE`, …) → `command_payload IS NULL`. +- A fixed operator-supplied value (e.g. `SET_POWER_LIMIT` to exactly `0`, or to `300`) → + `{ mode: 'STATIC', value: 300 }`. `value` may be a number, string, or object depending on the + command. +- A per-meter dynamic value, resolved at execution time (e.g. "restore each meter's own HPS-mode + limit") → `{ mode: 'RESOLVED', resolver: 'METER_OWN_HPS_LIMIT' }`. + +`RESOLVED` payloads are resolved through a small, named, unit-testable **resolver registry** in +backend code — the same "data-driven, named policy" shape ADR-002a already established for +supersession/triage rules: + +```ts +const COMMAND_VALUE_RESOLVERS: Record unknown> = { + METER_OWN_HPS_LIMIT: meter => meter.power_limit_hps_mode, +}; +``` + +This single mechanism covers today's power-limit case and any future command needing a number, +string, object, or per-meter lookup, without a schema migration per new case. For `SET_POWER_LIMIT` +specifically, the resolved/static value is written into the existing +`meter_interactions.target_power_limit` column at fan-out time (no schema change needed there). + +Validation that a `command_payload`'s shape matches what its `command_type` requires (e.g. +`SET_POWER_LIMIT` needs a payload, `TURN_ON` needs none) is a plain in-code TypeScript lookup used +at request-validation time — **not** a database table, and **not** a check on which +`command_type`/`target_meter_type` combinations are "allowed" (see decision 4). + +### 3. Explicit `target_meter_type` replaces implicit dual-meter branching + +A new nullable `target_meter_type: meter_type_enum` column (`HPS` / `FS`) filters which meters a +batch applies to; `NULL` means all meters, no filter. It applies uniformly to every `command_type`, +not just control commands — e.g. "read voltage on FS meters only" is equally expressible. + +This is an explicit, interim mechanism using the enum that already exists on `meters`, standing in +for full group-based targeting (decision 5) until that lands. It is documented here as a stopgap: +once `meter_groups`/group-based targeting exists, `target_meter_type` is superseded by +`target_meter_group_ids` (or equivalent), and this column can be retired. + +### 4. No upfront gating of command/target combinations + +There is no compatibility check rejecting e.g. `TURN_ON` on a grid without a wired relay, or +`SET_POWER_LIMIT` targeting `HPS` meters. Restricting *which* meters a command applies to is a +legitimate operator decision (there is no reason, for example, an operator shouldn't be able to +hard-cut every meter on a grid for an emergency shutdown) — the previous implicit +"`fs_command` always means `FS`-type meters" behavior was a business default, not a physical +constraint, and baking equivalent defaults back in as *validation* would just relocate the +conditional logic this ADR removes. `target_meter_type` makes the selection explicit instead of +inferring it. + +### 5. Full meter-grouping is deferred; the shell is documented, not built + +Selecting meters by group (tariff plan, connection type, location, or dimensions not yet known) is +**out of scope for this ADR's implementation**, but the target shape is recorded so that when it +lands, it composes with decisions 1–4 without another schema rework of `meter_command_batches`: + +- A first-class `meter_groups` table (id, org/grid scope, name, description) and a many-to-many + `meter_group_memberships` join table. Groups are named, describable things independent of *why* a + meter belongs to one. +- `meter_command_batches` targets group(s) by ID (exact column shape TBD at implementation time — + a join table or an ID array), never raw filter criteria. Execution code never needs to know how a + meter became a member of a group. + +How membership is computed is the open axis, recorded here as a spectrum from cheapest to most +capable — no commitment is made beyond documenting it: + +| Level | Mechanism | Notes | +|---|---|---| +| 1 | Flat `meters.tags: text[]`, matched by array overlap | Cheapest; no first-class group entity, rejected as the base design given the desire for named/manageable groups | +| 2 | **Manual membership** — `meter_groups` + `meter_group_memberships`, assigned explicitly (admin action / script) | **Recommended starting point** once this phase begins. Zero automation; membership is exactly whatever was assigned | +| 3 | **Derived membership** — same tables as Level 2, but a sync job (re)computes membership from `connections.tariff_plan`, connection type, pole location, etc. | Population-mechanism upgrade only; execution/query code unchanged from Level 2 | +| 4 | **Rule-based dynamic groups** — `meter_groups.membership_rule: jsonb`, a small filter DSL evaluated live at query time | No sync job, always fresh; requires building and curating a safe filter engine over whitelisted joinable fields | +| 5 | **Composable multi-dimensional segmentation** — groups carry a `dimension` (`TARIFF`, `CONNECTION_TYPE`, `LOCATION`, …); batches combine dimensions with AND/OR/NOT | Full segmentation product; most of this is speculative today | + +`meters.meter_type` (`HPS`/`FS`) is expected to eventually be absorbed into this future grouping +system (it currently conflates a hardware/wiring fact with a tariff-adjacent classification), but +this ADR does not change it — it is kept as-is (tariff modeling is undecided) and reused as-is for +`target_meter_type`. + +--- + +## Consequences + +### Positive + +- Removes the two-column purpose split and the `fs_command`-vs-`task_type` branch in `executeRule` + entirely; a batch's behavior is fully determined by `command_type` + `command_payload` + + `target_meter_type`, no derived/inferred state. +- Removes the runtime dependency on `grid.uses_dual_meter_setup`, unblocking its already-confirmed + baseline drop (register #17/§7). +- `command_payload`'s resolver-registry mechanism generalizes to any future command needing a + static or per-meter-dynamic value, without further schema migrations. +- `target_meter_type` gives immediate, real targeting flexibility (ALL/HPS/FS) using data that + already exists, while leaving room for the eventual group system without having to predict its + shape now. + +### Negative / Risks + +- `target_meter_type` is an admitted stopgap; it will need a follow-up migration to + group-based targeting once that lands, including a data migration for any batches created in the + interim. +- Fully permissive command/target combinations (decision 4) mean a misconfigured batch (e.g. + `TURN_ON` targeting `ALL` on a grid with no relay-controllable meters) fails silently at + execution (zero meters matched) rather than at creation time. Accepted as the cost of not + reintroducing hardcoded capability assumptions; revisit if this proves to be a frequent + operator error in practice. +- `meter_interaction_type_enum` reuse means `command_type` is not curated at the schema/type level — + the UI is the only thing preventing nonsensical batch commands (e.g. `TOP_UP`, `JOIN_NETWORK`) from + being scheduled. Acceptable given `meter_command_batches` already trusted its callers similarly. + +### Explicitly out of scope (tracked separately, not blocking this ADR) + +- **Grid-level FS intent fields** (`grids.should_fs_be_on`, `is_fs_on`, `should_fs_be_on_updated_at`) + no longer correspond to a coherent concept once targeting/values are arbitrary. Per ADR-002a's + intent/reality model, meter-level `should_be_on`/`power_limit_should_be` are the real source of + truth and the grid-level fields were always a cache. Deprecating them is a separate follow-up + migration — it has real blast radius (qilin `FsControlPredictor`/`FsControlSlider`, + `AutopilotService.eval`'s `CURRENT_FS_STATE_INJECTED`, and grid notification toggles + `is_upcoming_fs_control_rule_notification_enabled` / `is_fs_control_rule_change_notification_enabled` + all read them today). +- **Data backfill.** Existing `fs_command` rows are not migrated; operators recreate any needed + scheduled rules manually after cutover. +- **Frontend.** pegasus (`MeterTaskBatcher`, `FsControlView`) and qilin + (`FsControlPredictor`/`FsControlSlider`) are not updated as part of this ADR. Backend-only. +- **Audit trail.** `audits.from_fs_command` and the hardcoded ON/OFF message wording in + `DirectiveBatchService.createAudits` need to generalize (structured `command_type`/target fields + instead of a boolean flag), but this is folded into a separately-planned, more structured audits + data model rather than solved here. +- **`is_manual_mode_on` scope.** This flag's purpose was largely "exclude a meter from automated FS + control." With FS control as a distinct concept disappearing, whether/how it should apply across + all `command_type`s (today it's only enforced for `fs_command` batches, not scans) is an open + question for the implementation plan, not resolved by this ADR. +- **Schema tracking docs.** `docs/plans/002-oss-migration/002b-schema-*` are maintained by the + repo maintainer directly and are not updated as part of this ADR. + +--- + +## Triggers (revisit when) + +- Group-based targeting (decision 5) is actually scheduled for implementation — resolve the + membership-mechanism level (starting at Level 2, per the recommendation above) and design the + exact `meter_command_batches` → group reference shape. +- `target_meter_type` needs a third value or a multi-select — a signal that the group system should + be pulled forward rather than extending the stopgap further. +- Operators report frequent silent no-op batches from invalid command/target combinations (decision + 4) — revisit whether some minimal capability validation is warranted after all. +- The audits data-model overhaul is scoped — fold in the `from_fs_command` generalization then. + +--- + +## Related + +- **ADR-002** — meter state management crossroads (intent vs. reality framing). +- **ADR-002a** — reconciliation controller pattern; source of the named-policy-registry pattern + reused here for `COMMAND_VALUE_RESOLVERS`. +- **Schema deviation register #16** (`docs/plans/002-oss-migration/002b-schema-deviation-register.md`) + — the already-confirmed `directive_batches`/`directive_batch_executions` → + `meter_command_batches`/`meter_command_batch_executions` rename this ADR builds on. +- **Schema deviation register #17/§7** — the already-confirmed `grids.uses_dual_meter_setup` drop + this ADR's design unblocks. diff --git a/docs/architecture/012-company-cutover-strategy.md b/docs/architecture/012-company-cutover-strategy.md new file mode 100644 index 0000000..7116cf1 --- /dev/null +++ b/docs/architecture/012-company-cutover-strategy.md @@ -0,0 +1,165 @@ +# ADR-012: Company Cutover Strategy + +**Date:** 2026-07-10 +**Status:** Accepted (strategy-level); detailed runbook deferred to the just-in-time "Parity +verification & company cutover" sub-plan (see `docs/plans/002-oss-migration.md`) + +--- + +## Context + +ADR-008 sets the exit condition for the OSS migration tersely: the public repo reaches functional +parity → the company cuts over to running it → the private repo is retired, window kept +deliberately short. It does not decide *how* that flip is executed. ADR-009 governs how schema +migrations get applied (explicit, operator-controlled) but likewise does not cover the broader +system flip — backend, frontends, and third-party integrations together. + +A sanity-check pass (2026-07-10, alongside the 002b database-baseline wrap-up) surfaced facts about +the actual runtime shape that materially change what a safe cutover looks like, and that were not +available/considered when ADR-008 was authored: + +- **Host consolidation:** the 4 legacy apps (`tiamat`, `talos`, `loch`, `yeti`) collapse into **2** + runtime hosts per ADR-004/ADR-005 — `api` (`tiamat` + folded-in `talos`) and `worker` + (background/collector domains, `loch` + `yeti`). Cutover is a 2-deployable flip, not a 4-app one. +- **The database is the primary integration mechanism** (ADR-005 Accepted: shared DBs carry state; + residual HTTP is exceptional and prefer worker→`api`). The legacy bidirectional HTTP mesh is + retired as a pattern and shrinks further as hosts merge. + Beyond the backend, **all 5 frontends** (each with a single `VITE_API_URL` *and* direct + `VITE_SUPABASE_URL`/anon-key access, bypassing the API), **Grafana** (`grafana_readonly` role), + and **Make.com** (`make_readonly` role + grid webhook triggers) all depend directly on the same + Postgres instance. None of these non-backend consumers were analyzed against the schema deviation + register's rename/drop entries before now. +- Company cutover (roadmap "Deployment consumers" step 4) is the **only** step in the OSS migration + that touches the real production database — steps 1–3 (local, fresh Supabase project, early + adopter) run against isolated databases with no production risk. + +## Decision + +### 1. Split schema convergence into "anytime" and "flip-atomic" buckets +A single production database cannot run two divergent schemas at once, so the schema deviation +register's entries are triaged by cutover risk, not applied as one big-bang migration on cutover +day: + +- **Anytime, ahead of cutover:** additive/hardening changes (new indexes, function volatility, + `GRANT`s, `ON DELETE` hardening, …) and drops of objects re-verified dead against the *live* + production codebase (not just `legacy/`) — applied to production independent of, and well before, + backend cutover. +- **Flip-atomic only:** renames/type-changes still referenced by name by the currently-running old + code. These are the only changes that must happen in lockstep with stopping the old backend and + starting the new one. + +This minimizes what has to happen inside the actual cutover window to the smallest possible set. + +### 2. `api` and `worker` have different flip mechanics +- **`api`** (HTTP-facing) can be **blue/green**: old and new may run briefly in parallel; traffic + flips atomically via load balancer / the shared `VITE_API_URL` config point. +- **`worker`** (background/collector — queue consumers, cron) must be a **hard stop-then-start**: + running old and new simultaneously against the same queue or schedule risks double-processing. + There is no dual-run window for `worker`. + +### 3. Non-backend consumers are in scope for the rename/drop audit +Any register entry that renames or drops something touched directly by a frontend's direct Supabase +access, a Grafana dashboard/query, or a Make.com scenario must have its replacement staged and ready +to activate in the same cutover window — not discovered or built under pressure during the window +itself. + +### 4. RLS parity is a named, explicit verification requirement +Given the admin-organization redesign (schema deviation register #22) and the RLS invocation-pattern +rewrite (register #32), cutover requires an explicit per-role regression pass against a production +clone before go-live. The "same boolean result" reasoning recorded in the register at design time is +necessary but not sufficient — it must be verified against real data and real roles before the flip. + +### 5. Hard point of no return +No rollback path is designed past the flip-atomic migration step. A PITR/backup checkpoint is taken +immediately before it runs; past that point, forward-fix is the only path. This makes explicit (and +binding) the consequence of ADR-009's existing "backup / PITR checkpoint before destructive changes" +safety rail, rather than leaving it implied. + +### 6. A short maintenance window is acceptable +NXT Grid has confirmed a brief (minutes), scheduled maintenance window is an acceptable cost for the +cutover. This removes the need to design a fully zero-downtime flip and is the main simplifying +assumption behind decisions 1–2. + +### 7. Geo FastAPI is out of scope +The Python Geo FastAPI service (referenced by frontends via `VITE_GEO_API_URL`) is confirmed +unaffected by this migration and needs no coordination at cutover. + +### Illustrative sequence (non-binding) + +Recorded for continuity — this is **not** a committed plan. The authoritative runbook is the +just-in-time "Parity verification & company cutover" sub-plan (roadmap sub-plan index, last row), +authored once capability imports are nearly complete, per the roadmap's own rule that just-in-time +sub-plans describe reality rather than speculation. + +0. Confirm parity prerequisites: every capability NXT Grid runs today has completed its two-pass + import (ADR-008 Phase 3), respecting the hard edges; plan 001 (device-messaging extraction) at + the maturity the Metering import needs (roadmap standing assumption 1). +1. Apply every "anytime" convergence change (decision 1) to production ahead of time. +2. Audit remaining flip-atomic renames against every direct consumer (decision 3); pre-stage + replacement dashboards/frontend changes. +3. Rehearse the flip-atomic migration plus the new `api`/`worker` against a production clone + (Supabase branch / PITR snapshot), including the RLS regression pass (decision 4). +4. Execute the maintenance window: freeze deploys → stop old writers (`worker` hard-stop; `api` + optionally blue/green) → apply the flip-atomic migration (PITR checkpoint immediately before, + decision 5) → deploy new `api`/`worker` against the same production DB → flip `VITE_API_URL` + (one point, covers all 5 frontends) → activate pre-staged Grafana/Make.com updates → smoke test + → resume traffic. +5. Post-cutover: bake period with elevated monitoring, then decommission old infra, retire the + private repo (ADR-008 exit condition), close out the roadmap. (ADR-005 inter-host policy was + locked 2026-07-17 — flip coordination stays DB-primary with residual HTTP under that ADR.) + +## Consequences + +### Positive +- Cutover-day risk is minimized by front-loading everything that doesn't require coordination. +- The `api`/`worker` flip-mechanics asymmetry is decided ahead of time instead of discovered + mid-execution. +- Rollback expectations are explicit and backed by a concrete safety rail (PITR checkpoint), not + assumed. +- Frontends, Grafana, and Make.com are treated as first-class schema consumers, not an afterthought + to backend-only analysis. + +### Negative / Risks +- The hard point of no return means a bug discovered shortly after cutover must be forward-fixed + under time pressure, not rolled back. +- The illustrative sequence is not yet validated against real capability-import outcomes and will + need reconciling once the just-in-time sub-plan is authored. +- Requires disciplined use of the schema deviation register's "cutover implication" field to keep + the anytime/flip-atomic triage accurate as more entries are added over the life of the migration. + +## Rejected Alternatives + +- **Zero-downtime flip (no maintenance window).** Rejected — the database-as-integration-bus + constraint plus `worker`'s hard-stop requirement would make a fully zero-downtime flip materially + more complex (dual-schema compatibility shims, consumer-group coordination) for a cost NXT Grid + has confirmed it doesn't need to pay (decision 6). +- **New production database + full data migration at cutover**, instead of converging the existing + production database in place. Rejected — ADR-008 already commits to in-place convergence ("company + DB is temporarily a superset"); re-deciding this would be a much larger, unnecessary undertaking. + +## Triggers (revisit when) + +- The just-in-time "Parity verification & company cutover" sub-plan is authored — reconcile this + ADR's illustrative sequence with it; the sub-plan supersedes it where they conflict. +- ADR-005 was Accepted (2026-07-17) — flip coordination remains DB-primary; residual HTTP follows + that ADR (prefer worker→`api`; no mandatory broker). Revisit only if a recorded exception + changes cutover sequencing. +- The maintenance-window tolerance changes (e.g., a new integration is added that cannot tolerate + any downtime), invalidating decision 6. +- **NXT Grid production Postgres major-version lag** — OSS baseline targets **Postgres 17** (002b + complete); company production is still on **PG15** (2026-07-10). A separate Supabase + platform-upgrade project must complete before cutover can assume PG17 parity (extension set, + deprecated extensions, custom-role password re-hash if any). Parent roadmap assumption **#11**; + not 002b/002c scope. + +## Related + +- **ADR-004** — target architecture; the `api`/`worker` host consolidation this ADR's flip mechanics + depend on. +- **ADR-005** — inter-host communication (Accepted); shared DBs + residual HTTP policy for + `api`/`worker` during and after cutover. +- **ADR-008** — migration strategy; the exit condition this ADR operationalizes. +- **ADR-009** — migration deployment & governance; the safety rails (PITR, forward-only) this ADR + builds on. +- **`docs/plans/002-oss-migration.md`** — roadmap; sub-plan index entry for the eventual detailed + runbook. diff --git a/docs/architecture/013-capability-owned-behavior-over-shared-core-entities.md b/docs/architecture/013-capability-owned-behavior-over-shared-core-entities.md new file mode 100644 index 0000000..54ba8a8 --- /dev/null +++ b/docs/architecture/013-capability-owned-behavior-over-shared-core-entities.md @@ -0,0 +1,110 @@ +# ADR-013: Capability-Owned Behavior over Shared Core Entities + +**Date:** 2026-07-15 +**Status:** Accepted + +--- + +## Context + +ADR-004 establishes a modular monolith: a lean always-on **platform core** (reconciled to +**Foundation** during the 002d import — see `docs/plans/002-oss-migration/002d-platform-core-import.md`) +plus **capability modules** gated by config. Foundation owns the shared identity/data graph +(organizations → grids → sites, accounts, members, api-keys); capabilities (metering, payments, +production, …) are turned on and off per deployment. + +This raises a recurring design question, first surfaced while scoping the `grids` import (002d): +**where does behavior that *involves* a core entity but *belongs* to a capability live?** A `grid` +row is Foundation data, but "grid connectivity statistics" is a *metering* concern that merely +reads grids. The tempting-but-wrong answer is to let the core `grids` module grow metering methods +that light up when metering is enabled. Two concrete framings drove the decision: + +- *"When an adopter turns metering on, does the grid suddenly gain more methods?"* +- *"If an adopter runs metering but does not want historical connectivity data persisted to + Timescale (cost), how does that split?"* + +Both are really the same question about **module boundaries**, and the wrong resolution leaks +capability logic into core, couples core to capabilities (inverting the dependency direction), and +makes disabled-capability behavior a conditional inside always-on code rather than simply-absent. + +This ADR is deliberately **guiding for all future development**, not just the migration. + +## Decision + +### 1. Behavior follows domain ownership, not the entity + +Core entities are shared **data**. Behavior that is specific to a capability lives in the +**owning capability**, which reads/writes the shared core data — it is **never** bolted onto the +core module. Core modules expose the entity's own identity/CRUD (create/read/update/list of the +row itself); everything a capability *does* with that row lives on the capability's surface. + +**Worked example (grids):** Foundation's `grids` module owns grid CRUD/read. Metering owns +connectivity statistics and exposes them on its **own** surface (e.g. +`GET /metering/grids/:id/connectivity` from within the metering module), reading the shared grid +data. Grids-core stays lean; it does not import or know about metering. + +### 2. No conditional-unlock methods on core modules + +A core module's surface does not change based on which capabilities are enabled. Behavior is not +"unlocked" on core when a capability turns on — the capability module simply *is or is not +composed* (Tier-1 gating, ADR-007). When a capability is off, its endpoints/services do not exist +because the module was never wired, not because a branch inside core skipped them. The dependency +edge always points **capability → core**, never the reverse. + +### 3. Split capabilities along cost/independence seams, not by entity + +When a capability has parts an adopter may legitimately want independently, split it into +**cohesive sub-modules along that seam** — not by threading options through a shared entity's +module. Illustrative: metering that persists historical connectivity data to Timescale vs. metering +that does not splits into a `metering` module and a `metering-monitoring` module (the latter owning +the Timescale persistence). The `grid` entity gains nothing in either case; the seam lives in the +capability layer where the cost/independence actually is. + +### 4. No plugin/extension registry in the baseline + +Core does not ship a registry that capabilities register behavior into (a plugin bus, a +`registerGridAction()`-style hook, etc.). That is an indirection with no current need and it +re-introduces the core→capability coupling this ADR removes. Composition is explicit and static +per host (ADR-007 §7 as amended). Revisit only if a real extensibility requirement appears (see +Triggers). + +## Consequences + +### Positive + +- Core stays lean, stable, and free of capability knowledge; the dependency graph is acyclic + (capability → core only). +- Disabled capabilities are genuinely absent (smaller surface, fail-fast at boot), not dormant + branches inside always-on code. +- "Whole-module vs split-module" for future imports has a principled answer: split along + cost/independence seams in the capability layer. +- Behavior is discoverable by domain: to find what the platform *does* with a grid, look in the + owning capability, not in an ever-growing core module. + +### Negative / Risks + +- A capability that reads core data must go through core's surface/services rather than reaching + into core internals — a little more ceremony than a single fat module. +- Requires judgment at import time to route entangled legacy behavior to the right owner (mitigated + by the migration's per-behavior import ledger and "no-cracks" governance in the roadmap). +- Some cross-capability data needs (e.g. payments reading metering facts) surface as soft pairings + (ADR-004 decision 6), which must degrade gracefully rather than hard-couple. + +## Related + +- **ADR-004** — capability map and Tier-1 gating; §5 (as amended) narrows platform core so that + capability-specific modules (agents, dcus, poles, websocket, routers, download) are *not* + Foundation. +- **ADR-007** — configuration & wiring; §7 (as amended) explicit static per-host composition is the + mechanism that makes disabled capabilities simply-absent. +- **002d — Platform Core Import** — first application of this principle (grids is a partial import; + metering-coupled grid behavior is re-homed to Metering, not restored to core). + +## Triggers (revisit this ADR when) + +- A genuine third-party/extension requirement appears where external code must add behavior to a + core entity without modifying core (reconsider decision 4's "no registry"). +- A capability seam cannot be expressed as cohesive sub-modules and genuinely needs per-entity + optionality (reconsider decision 3). +- Two capabilities need to share non-core behavior over the same entity (introduce a shared + capability or a port, rather than pushing behavior down into core). diff --git a/docs/architecture/014-api-key-and-machine-credentials.md b/docs/architecture/014-api-key-and-machine-credentials.md new file mode 100644 index 0000000..4884625 --- /dev/null +++ b/docs/architecture/014-api-key-and-machine-credentials.md @@ -0,0 +1,142 @@ +# ADR-014: Machine Credentials — API Keys, Scopes, Postgres Roles, and MCP + +**Date:** 2026-07-17 +**Status:** Accepted (direction); implementation lands incrementally with consumers +**Related:** ADR-004 (Foundation / capabilities), ADR-007 (wiring / secrets), 002d Foundation +import (`auth`, `api-keys`), schema deviation register #2/#3 (company readonly roles) + +--- + +## Context + +Foundation authenticates humans via Supabase JWTs (bearer) and machines via `X-API-KEY` +(`public.api_keys`). The API-key path currently resolves a key to an `AuthenticatedUser` +(account / org / member claims) using the **admin** Supabase client and does **not** attach a +per-request user client. Handlers that then use `service_role` bypass RLS. + +That is acceptable for a few trusted local/dev machine keys. It becomes dangerous as: + +- More integrations need narrow, specific data. +- MCP (and similar agent) servers grow tools and call many endpoints. +- The same powerful key (e.g. platform-operator SUPERADMIN) is reused for every tenant’s MCP + session, with “who may see what” enforced only in application code. + +Separately, the legacy company database provisioned **Postgres login roles** +(`grafana_readonly`, `make_readonly`, `snaplet_readonly_2`) for direct DB access. Those are a +different credential family from Nest API keys and were **omitted from the OSS baseline** +(parameterized company infra). + +This ADR records the credential model, when to use which mechanism, and the hardening path — +including for MCP’s two audiences (internal platform ops vs customer-org). + +### Why this document is in the open repo + +Security **design** (roles, RLS, scopes, least privilege) is already implied by schema and auth +code. Publishing the strategy: + +- Helps adopters avoid shipping a single god-key. +- Is not a substitute for secrets management; **keys, passwords, and company recipes stay out of + git**. + +Do **not** put production key material, allowlists of real integrations, or exploit runbooks here. +Company-specific Postgres role recipes remain under operator docs (e.g. +`docs/database/optional/`), not the generic baseline migration. + +--- + +## Decision + +### 1. Three credential layers (do not conflate “role”) + +| Layer | What it is | Examples | RLS? | +|---|---|---|---| +| **A. Postgres login roles** | DB users that connect with a DB password (Grafana, Make, Snaplet, …) | `grafana_readonly`, `make_readonly` | Policies may target the role; often `USING (true)` read-all for that tool | +| **B. Supabase Data API JWT roles** | Role PostgREST assumes from the JWT | `anon`, `authenticated`, `service_role` | `authenticated` / `anon`: RLS applies. `service_role`: **RLS bypassed** | +| **C. App API keys** | Product machine credentials in `api_keys`, validated by Nest | `X-API-KEY`, seed `dev-api-key-…` | Only if Nest then uses a **user-scoped** client (B=`authenticated`) with that principal’s claims | + +Nest `SupabaseService.adminClient` is layer **B / `service_role`**. Bearer auth’s per-request +client is **B / `authenticated`**. API keys are layer **C** until mapped onto B. + +### 2. When to use which strategy + +| Use case | Prefer | Avoid | +|---|---|---| +| Human operators / dashboards (Pegasus, etc.) | Bearer JWT → `authenticated` + RLS | Long-lived API keys for interactive use | +| Narrow machine job needing one slice of data (report, webhook, single integration) | **Scoped API key** on a least-privilege service account; route allowlist; prefer RLS-bound session | Platform SUPERADMIN key; admin client for the whole request path | +| MCP / agent tools — **customer** (own org’s grids) | Per-tenant (or per-customer-user) session: org-bound claims + **RLS**; scopes limited to MCP tool surface | One shared platform key for all customers; “filter org in MCP code only” | +| MCP / agent tools — **internal team** (cross-org / all grids) | Dedicated **platform-ops** service principal (or platform-operator membership) with **explicit** broad scopes; few keys; audited | Reusing a human SUPERADMIN’s personal key; silent `service_role` for every tool | +| BI / Grafana / ETL reading many tables directly | Postgres role (**A**) + optional `TO ` RLS policies; company recipe | Nest API keys “because we already have them” | +| DB cloning / Snaplet-style tooling | Postgres role with needed schema grants (legacy pattern); company recipe | Service role in app config handed to third-party SaaS carelessly | +| Pre-auth / privileged Nest internals (validate API key row, invite user, …) | Admin client (`service_role`) **only** for that privileged step | Admin client as default for all handlers after auth | +| Break-glass / migrations | `service_role` or postgres superuser, offline or tightly gated | Everyday MCP or integration traffic | + +### 3. Scopes on API keys + +Scopes answer **which capabilities/routes** a key may invoke (e.g. `mcp:grids:read`, +`reports:revenue:read`). They grow with MCP tools and must be **granted deliberately**. + +Scopes do **not** replace tenancy. “Which org’s rows?” remains JWT / `app_metadata` claims + RLS +(or an explicit platform-ops policy set). + +**Growth rule:** new MCP tool → new scope(s) → grant only to principals that need them. Internal +ops keys may accumulate broader scopes; customer keys stay on a small allowlist. + +### 4. MCP must not share one god-key across tenants + +If one platform API key backs MCP for every customer and the server filters access in app logic, +that is **authorization in the app instead of (or duplicated poorly beside) RLS**. It does not +scale: every new tool must remember checks; agents can be steered into over-fetching. + +**Target:** + +- **Customer MCP:** session identity = that org (customer key or short-lived token); DB via + `authenticated` + RLS. +- **Team MCP:** separate platform-ops principal; broad scopes by design; still prefer + RLS-bound policies for platform ops over blanket `service_role` for tool handlers. + +### 5. Hardening direction (implementation order) + +1. **Policy (now):** Never issue platform-operator SUPERADMIN keys to customer MCP or narrow + integrations. Dedicated service accounts; seed platform key stays **local/dev only**. +2. **Route allowlist:** API-key (and later scoped) principals may only hit approved surfaces + (e.g. MCP / machine routes), not the full human API by default. +3. **RLS-bound machine sessions (near-future — do next for API-key hardening):** After API-key + (or token-exchange) auth, attach a user-scoped Supabase client with that principal’s JWT + claims (`AuthenticatedUser.supabase`) so request handlers can exercise RLS — **not** admin — + except true privileged steps. Today `ApiKeyStrategy` returns claims only (no client); + machine handlers that need PostgREST fall through to `service_role` (high privilege). Confirmed + during 002d Task 9 (`user-admin` / create-customer machine callers). Land this **before** + growing more machine-callable data paths (e.g. Task 10 grids reads). Privileged Auth Admin + surfaces may stay whole-method admin regardless. +4. **Scopes on `api_keys`:** Persist and enforce scopes in Nest (guard/interceptor). Taxonomy + owned by Foundation + each capability that exposes machine APIs. +5. **Optional later:** Short-lived token exchange (key → JWT with scopes + expiry); separate MCP + gateway host; OAuth2 client-credentials for external MCP clients. + +### 6. Postgres readonly roles stay company-optional + +`grafana_readonly` / `make_readonly` / `snaplet_*` remain **out of the OSS init migration**. +Operators who need them apply recipes; they are not a substitute for designing Nest API keys or +MCP tenancy. + +--- + +## Consequences + +- Adopters get a clear map: direct DB tools ≠ Nest API keys ≠ Supabase `service_role`. +- MCP product work must budget for **per-tenant credentials** (or equivalent sessions), not only + tool implementations. +- Foundation will grow `api_keys` (scopes) and auth middleware; capabilities register scopes for + their machine endpoints rather than inventing parallel auth. +- Until items 3–4 land, treat existing API keys as **high privilege** and minimize their use and + distribution. + +--- + +## Notes + +- Seed key `dev-api-key-platform-superadmin` is for local Foundation auth tests only. +- Manual API checks: `apps/api/http/` (httpYac); prefer bearer for human-shaped tests. +- Related omission: schema deviation register #2 / #3 (Grafana / Make roles). +- 2026-07-17 — §5.3 elevated to **near-future** after 002d Task 9: pointer also on + `apps/api/.../auth/api-key.strategy.ts` and 002d decisions log. diff --git a/docs/deployment/digital-ocean-buildpack.md b/docs/deployment/digital-ocean-buildpack.md new file mode 100644 index 0000000..3a3e121 --- /dev/null +++ b/docs/deployment/digital-ocean-buildpack.md @@ -0,0 +1,48 @@ +# Deployment on DigitalOcean (App Platform, buildpack) + +Branch-based deploy for one DO app with one or more components from the monorepo root. + +## 1. Create the app +Create the app from the version you want to build from. If you're setting up a fresh app, you most likely will want to use the latest version. + +[DigitalOcean App Platform](https://cloud.digitalocean.com/apps) → **Create App** → connect +GitHub → repo root, branch **`your-selected-version-branch`**. + +Do NOT go with the standard settings of Digital Ocean. You should carefully review these: +- Check the name!! It usually automatically sets it to nxt-backend (the name of the repo), but you want to give it a custom name like `api`, or `[your-company]-api`. +- The size is usually set too large, so select a smaller instance size to start with. You can scale up later as desired. +- Change the deployment settings! This is not the guide for a Docker build, which is the default setting. Set the build strategy to Node.js Buildpack. + +For the API, the build command is: +```bash +corepack enable && pnpm install --frozen-lockfile && pnpm exec nx sync && pnpm exec nx build api +``` +and the run command is: +```bash +`node apps/api/dist/main.js` +``` + +## 2. App-wide environment variables + +Set at **app level** so all components inherit them (DO: Settings → App-Level Environment +Variables). + +| Variable | Value | Scope | Notes | +|---|---|---|---| +| `NODE_ENV` | `production` | Build & Run | Standard for both components. | +| `NX_DAEMON` | `false` | **Build time** (Build & Run is fine) | Stops Nx from writing `.nx/workspace-data/…/daemon.log` during the build; avoids buildpack export / DOCR layer errors. Not required at runtime. | + +## 4. Verify + +**`api`** — public URL: + +```bash +curl -sS "https:///health" +``` + +Expect **200** and JSON with `name` and `version`. + +**`worker`** — Runtime Logs: periodic `heartbeat` lines from `@nestjs/schedule`. + +## Health checks +/health diff --git a/docs/deployment/supabase.md b/docs/deployment/supabase.md new file mode 100644 index 0000000..93e6a50 --- /dev/null +++ b/docs/deployment/supabase.md @@ -0,0 +1,91 @@ +# Deployment on Supabase + +## 1. Create an account and start a new project on Supabase + +[Supabase](https://supabase.com/) + +## 2. Create a new organization + +In the dashboard. (If you don't already have one.) + +## 3. Create a new project + +When creating a project, choose a suitable name, and make sure 'Enable Data API' is checked. +Also make sure that 'Automatically expose new tables' and 'Enable automatic RLS' are NOT checked. +Check the location too, + +## 4. Connect and apply migrations + +From the repo root (Supabase CLI pinned in `package.json`; make sure you have run `pnpm install` +first). Prefer `pnpm exec supabase` so you always hit the pinned binary: + +```bash +pnpm exec supabase login # If not already logged in +pnpm exec supabase link --project-ref +pnpm exec supabase db push +``` + +### Local development + +```bash +pnpm exec supabase start # applies migrations on a fresh local DB +pnpm exec supabase db reset # recreates DB, re-applies migrations, then runs seed.sql +``` + +`supabase/config.toml` has `[db.seed]` enabled with `sql_paths = ["./seed.sql"]`. Seed runs on +**`db reset`**, not on every plain `start` against an already-initialized volume. + +## 5. Local-dev seed (Foundation bootstrap) + +Migrations ship **schema only**. For **local development**, Foundation fixtures live in +`supabase/seed.sql` and grow as capability imports land. This seed is **not** for remote/prod — +production still needs an operator-specific one-time bootstrap outside this file. + +### Reset and seed + +```bash +pnpm exec supabase db reset +``` + +That drops the local DB, applies migrations, then runs `supabase/seed.sql`. + +### What the seed creates + +| Fixture | Details | +|---------|---------| +| Orgs | `1` **NXT Platform Operator** (`PLATFORM_OPERATOR`) + wallet; `2` **NXT Solar Developer** (`SOLAR_DEVELOPER`) + wallet | +| Auth users | `superadmin@nxt-platform.com` / `superadmin`; `admin@nxt-solar.com` / `admin` (confirmed; fixed UUIDs in seed) | +| Accounts | Created by `handle_new_user` on auth insert; `organization_id` set via `handle_update_user` when `app_metadata` is applied | +| Claims | JWT `app_metadata`: `account_id`, `account_type` (`MEMBER`), `member_type`, `organization_id` | +| Members | Platform → `SUPERADMIN` (org 1); Solar → `DEVELOPER` (org 2) | +| API key | `dev-api-key-platform-superadmin` on the platform superadmin account (`X-API-KEY` / auth tests) | +| Grid | **Demo Solar Grid** on org 2 | + +Invite flow mirrored in SQL: insert auth user → account trigger → update `raw_app_meta_data` → +account `organization_id` sync → insert `members`. + +### Sign-in (local) + +Use the emails/passwords above against the local Auth API / Studio +(`pnpm exec supabase status` for URLs and keys). Prefer the seeded API key for machine auth +smoke tests (`X-API-KEY` / httpYac under `apps/api/http/`). Api auth also needs +`SUPABASE_PUBLISHABLE_KEY` plus `SUPABASE_JWKS_URL` (or `SUPABASE_JWT_SECRET`) in +`apps/api/.env` — see `apps/api/.env.example`. + +### REST / httpYac (manual API checks) + +File-based requests live under `apps/api/http/` using **httpYac** (`anweber.vscode-httpyac`). +Shared login via `# @import ./login.http` + `# @ref loginPlatform` — see `apps/api/http/README.md`. +Uninstall Huachao REST Client if present (conflicts on `.http` files). + +### Automated tests (`api`) + +Specs live under `apps/api/test/` (not co-located with `src/`): + +| Target | Command | Needs local Supabase + seed? | +|--------|---------|------------------------------| +| Unit (default / lint bar) | `pnpm exec nx test api` | No | +| Integration | `pnpm exec nx run api:test-integration` | Yes | +| E2E | `pnpm exec nx run api:test-e2e` | Yes | + +Suites under integration/e2e skip when `SUPABASE_URL` / `SUPABASE_SECRET_KEY` are missing. diff --git a/docs/plans/002-oss-migration.md b/docs/plans/002-oss-migration.md index a178589..cf6e524 100644 --- a/docs/plans/002-oss-migration.md +++ b/docs/plans/002-oss-migration.md @@ -1,7 +1,7 @@ # Open-Source Migration — Roadmap -**Decisions:** ADR-004 (architecture), ADR-005 (open — see decision points), ADR-006 (tooling/CI), -ADR-007 (config), ADR-008 (migration strategy), ADR-009 (migration governance) +**Decisions:** ADR-004 (architecture), ADR-005 (inter-host communication — Accepted 2026-07-17), +ADR-006 (tooling/CI), ADR-007 (config), ADR-008 (migration strategy), ADR-009 (migration governance) **Plan number:** 002 (family) **Created:** 2026-07-08 **Status:** In progress @@ -114,9 +114,16 @@ Step 0 (002a) ─┬─ Track A: Database baseline (002b) ─────── - **Track B (002c)** proves the golden path (`migration up → gen-types → typecheck → build → image → deploy`) per ADR-006/ADR-008 Phase 1, and establishes the ADR-007 config skeleton. Until Track A delivers, the golden-path proof may use a minimal hello-world migration. + **Completed 2026-07-14** (Tasks 1–11). Next: capability imports (002d…). - **Interlock:** the baseline migrations from Track A become the scaffold's canonical - `supabase/` content; types are regenerated from them and the type-drift CI guard goes live - against the real schema. Whichever track finishes first waits at this join. + `supabase/` content. **Adopted in 002c Tasks 4–5** (002b finished first). **Interlock reached + 2026-07-14** (002c Task 8 clean-clone sign-off). Type-drift CI guard deferred until + post-migration (002c Task 7). + +**002b interlock deliverables (Track A complete, 2026-07-13):** see +`002b-database-baseline.md` Task 10 handoff table — init migration at root `supabase/`, PG17, +CLI pin `2.109.1`, gen-types invocation (`--schema public` only), deviation register, deployment +doc. **Adopted in 002c Tasks 4–5; interlock reached 2026-07-14** (Task 8 sign-off). **Hard edges for capability imports (non-negotiable, ADR-008 Phase 3):** @@ -131,6 +138,10 @@ Two passes per module (ADR-008): (a) move it in working, behavior-preserving; (b port seam + capability flag. Each capability registers its own three-tier config into the 002c skeleton as it lands (ADR-008 Phase 4). Capability numbering per ADR-004 decision 5. +> **Foundation (002d) is a single-pass exception.** It is always-on, so it has no capability flag +> and (with only one auth provider) no speculative port seam — pass (b) does not apply. The dual- +> pass model resumes for the flagged capabilities from 002e onward. + **Import mechanics (whole vs partial):** - **Whole-module moves** are `git mv legacy/... → ...` with adaptation in a *separate follow-up @@ -144,6 +155,26 @@ skeleton as it lands (ADR-008 Phase 4). Capability numbering per ADR-004 decisio what has been re-homed, and guards against two capabilities unknowingly importing duplicate copies of shared entangled code. +### "No-cracks" governance (migration-scoped) + +The primary failure mode of an incremental import is legacy code that is neither imported nor +consciously deferred — it just gets forgotten. Four rules keep every part accounted for (read +during migration work; not always-on context): + +1. **Ledger completeness.** Every legacy file in a sub-plan's scope gets an import-ledger row — + `moved` / `partially imported (listing what remains)` / `superseded → deleted` / `deferred (with + destination)`. No file is silently skipped. The ledger, not git history, is authoritative. +2. **Per-behavior placement (ADR-013).** For an entangled module, each *behavior* is routed to its + owning capability in the ledger **before** the legacy file is deleted. A legacy file is deleted + only when **every** behavior it holds has a recorded home (imported or explicitly deferred). +3. **Legacy shrinks as the to-do list (rule 5 / ADR-008).** `legacy/` is never edited, only deleted + from, and only on **full** supersession. A non-empty `legacy/` *is* the standing list of what has + not yet been re-homed; `legacy/` reaching empty is part of the exit condition. +4. **Every deviation recorded.** Renames, drops, de-brand/i18n neutralizations, and behavior + re-homings land in the appropriate register (schema deviation / internationalization & de-brand) + or the sub-plan decisions log. Undocumented divergence is the failure this whole spine exists to + prevent. + | Order | Import | Driver | |---|---|---| | 1 | Platform core | Hard edge — everything depends on it | @@ -156,35 +187,44 @@ Sub-plans live in `docs/plans/002-oss-migration/`. Keep this table current. | ID | Title | Scope | Status | |---|---|---|---| -| 002a | Repo restructure (Step 0) | Create `oss-migration` branch; atomic rename-only move to `legacy/`, freeze notice, verification | Authored — not started | -| 002b | Database baseline | Inventory, four-bucket classification, canonical init migration, A/B diff verification (old chain from `legacy/supabase/migrations`), deviation register, staged rollout (local → fresh Supabase project → adopter) | Authored — not started | -| 002c | Scaffold, pipeline & config skeleton | Fresh Nx 23 workspace (ADR-006), CI with affected + type-drift guard, Dockerfile, DO deploy baseline, ADR-007 config loader/schema skeleton, hooks reintroduction | Authored — not started | -| 002d | Platform core import | Move platform-core modules into the new workspace (two passes) | Just-in-time — not yet authored | -| 002e | Energy Production Monitoring import | Capability (1), incl. TimescaleDB estate | Just-in-time — not yet authored | +| 002a | Repo restructure (Step 0) | Create `oss-migration` branch; atomic rename-only move to `legacy/`, freeze notice, verification | Completed | +| 002b | Database baseline | Inventory, four-bucket classification, canonical init migration, A/B diff verification (old chain from `legacy/supabase/migrations`), deviation register, staged rollout (local → fresh Supabase project → adopter) | **Completed** (2026-07-13) | +| 002c | Scaffold, pipeline & config skeleton | Fresh Nx 23 workspace (ADR-006), CI with affected + type-drift guard, Dockerfile, DO deploy baseline, ADR-007 config loader/schema skeleton, hooks reintroduction | **Completed** (2026-07-14) | +| 002d | Platform core import (Foundation) | Import the always-on Foundation (auth, api-keys, accounts, members, organizations, user-admin, grids) over infra (Supabase provider, HTTP, logging); retire ops-DB TypeORM; drop `deployment` config group; explicit per-host composition | **Completed** (2026-07-17) | +| 002e | Energy Production Monitoring import | Capability (1), incl. TimescaleDB estate; **exclude device registry** (register #12 — see assumption #10). **Prerequisite:** **ADR-005** (Accepted 2026-07-17) | Just-in-time — not yet authored | | 002f… | Remaining capability imports | (2) Metering, (3) Payments, (4) Notifications, (5) Field Ops, (6) Automation — one sub-plan each; IDs assigned when authored | Just-in-time — not yet authored | -| (last) | Parity verification & company cutover | Parity checklist, company DB convergence migration (from deviation register), cutover, private-repo retirement | Just-in-time — not yet authored | +| (last) | Parity verification & company cutover | Parity checklist, company DB convergence migration (from deviation register), cutover, private-repo retirement. Strategy-level decisions (host flip mechanics, rollback stance, maintenance window) recorded early in **ADR-012** — reconcile with it when authoring | Just-in-time — not yet authored | ## Standing assumptions and open decision points | # | Item | Standing position | Resolves when | |---|---|---|---| | 1 | **Device-messaging (plan 001 / ADR-010)** | Runs as a parallel effort; this migration treats device-messaging as **arriving as an external service**. The Metering import sub-plan depends on plan 001 being (near) complete | Checked when the Metering sub-plan is authored | -| 2 | **ADR-005 inter-host communication** | Deliberately open. Groundwork (002a–002c) does not need it; much of the current HTTP mesh collapses into in-process calls in the modular monolith | **Decide before the `worker` host receives its first real capability** (i.e. during/before the Production Monitoring import) | +| 2 | **ADR-005 inter-host communication** | **Accepted (2026-07-17).** Independent hosts; shared DBs carry state; residual sync HTTP (prefer worker→`api`, ADR-014 auth); async via per-capability DB jobs; retire bidirectional `*_API` mesh; no broker / `LISTEN`/`NOTIFY` baseline; capability-owned ops writes (ADR-013); device-messaging = integrable HTTP+callbacks (ADR-010) | **Done** — apply during 002e+ capability imports | | 3 | **TimescaleDB schema** | Out of the 002b baseline (Supabase primary DB only). Belongs to the Production Monitoring capability import, where its consumers live | 002e authoring | -| 4 | **Production schema = migrations** | Production has had no schema changes outside `supabase/migrations`. Certified once by a read-only `supabase db diff --linked` drift check at the start of 002b | 002b first task | +| 4 | **Production schema = migrations** | Production has had no schema changes outside `supabase/migrations`. Certified by read-only drift check at 002b Task 1 (2026-07-08) | Done (002b Task 1) | | 5 | **Adopter requirements surface during execution** | Outside requirements (renames, omissions, additions) are discovered *while executing* sub-plans, not gathered up-front — and always recorded (see below) | Continuous | -| 6 | **Branch strategy** | The migration lives on the long-running **`oss-migration`** branch; `main` keeps the original tree (incl. original README) untouched. No external automation watches this repo (production builds from the private repo). Documentation rewrites (root README, AGENTS.md commands) are deferred to the later phases | When/how the branch lands on `main` — decided in a later phase | -| 7 | **Git hooks (husky) suspended** | Hooks are suspended from Step 0 onward to avoid friction during groundwork. Automatic lint/typecheck on commit is reintroduced as a 002c task, activated once the new workspace's lint/typecheck targets are stable | 002c execution | +| 6 | **Branch strategy** | **Resolved after 002d (2026-07-18):** land Foundation on **`main`** via `oss-migration` → `main`; further capability imports and roadmap work continue on **`main`**. The long-running `oss-migration` branch was the migration vehicle while `main` held the pre-restructure tree; that tip remains in git history / `legacy/` — not a second live track. No external automation watches this repo (production still builds from the private company repo until ADR-012 cutover). Root README carries an honest Foundation-status blurb; fuller docs rewrites remain deferred. | **Done** — PR to `main` after 002d | +| 7 | **Git hooks (husky) suspended** | Hooks were suspended from Step 0 onward; reintroduced in 002c Task 10 (husky + lint-staged + `nx affected -t typecheck --uncommitted`) | **Done** (2026-07-14, 002c Task 10) | +| 8 | **Company cutover strategy (ADR-012)** | Decided ahead of the just-in-time sub-plan, since they're durable and unlikely to change: schema convergence splits into "anytime" (additive/dead-drop) vs. "flip-atomic" (renames) changes; `api` can blue/green but `worker` needs a hard stop-then-start; RLS parity gets an explicit regression pass; hard point-of-no-return past the flip-atomic migration (PITR checkpoint immediately before); a short maintenance window is acceptable; Geo FastAPI is out of scope | The "Parity verification & company cutover" sub-plan is authored — reconcile its runbook with ADR-012, which it supersedes on execution detail | +| 9 | **DB-native platform operator org** (register #22) | Admin organization row is DB-native (`PLATFORM_OPERATOR`). **Backend resolution superseded (002d, 2026-07-15):** the `deployment` config group is **dropped** entirely (`adminOrganizationId` + `systemWalletId`) — no config field; backend consumers resolve the admin org **DB-side within their owning capability** (Payments / Field Ops), all of which are deferred, so nothing in the Foundation reads it. `systemWalletId` returns as `bankingSystemWalletId` under `capabilities.payments`. **Frontend apps' resolution still not decided** | Backend: **resolved (002d)** — ADR-007 Amendment 2026-07-15 §B. Frontend delivery still open — **ADR-007** Amendment "Open / deferred"; revisit alongside decision 11's frontend config delivery | +| 12 | **Module-split philosophy (whole-module vs split)** | Resolved in principle: **behavior follows domain ownership, not the entity** — capability-specific behavior lives in the owning capability, never bolted onto a core module; split capabilities along cost/independence seams. Authoritative record: **ADR-013**. `user-admin` (imported whole incl. agents/customers) is a recorded per-module exception, not a precedent | **Decided (ADR-013, 2026-07-15)** — applied per import from 002d onward | +| 10 | **Device registry dropped from baseline** (register #12) | `devices` / `device_types` / `device_logs` excluded from OSS schema; **ADR-004** §5 amended (2026-07-13) — `device-data-sink` removed from (1) | **002e** authoring (import must not resurrect device registry) | +| 11 | **NXT Grid production Postgres 15→17** | OSS baseline targets PG17; NXT Grid production is still PG15 — independent Supabase platform-upgrade project (extensions, role passwords, …), not 002b/002c | Before company cutover parity — **ADR-012** trigger; prerequisites in cutover sub-plan when authored | ## Deviation recording -Two levels, both mandatory: +Three registers, all mandatory: 1. **Schema deviation register** (companion to 002b, in the sub-plan folder): one entry per deviation from the original schema — object, change (drop / exclude / parameterize / rename / other), rationale, and **cutover implication** (what the company DB needs to converge). This register *is* the spec for the company convergence migration at cutover. -2. **Per-sub-plan decisions log**: every sub-plan ends with a "Notes & decisions log" section +2. **Internationalization & de-brand register** (`internationalization-and-debrand-register.md`, + migration-wide): one entry per Nigeria-specific / NXT-Grid-specific value or name (currency, + timezone, phone/locale, brand strings, brand-named symbols) and how the baseline neutralizes, + configures, or defers it. Roadmap rule 6 made durable. Created in 002d. +3. **Per-sub-plan decisions log**: every sub-plan ends with a "Notes & decisions log" section (as in plan 001) recording task-level deviations and choices made during execution. **Rename policy:** renames are allowed but each one is a loan against later phases — imported @@ -194,10 +234,15 @@ cutover. Weigh each rename individually; record all of them. ## Related documents - **ADR-004** — target architecture; capability map; three-tier flags. +- **ADR-005** — inter-host communication (Accepted 2026-07-17); apply from 002e onward. - **ADR-006** — tooling/CI decisions executed by 002c. - **ADR-007** — config mechanism executed by 002c (skeleton) and each capability import (flags). - **ADR-008** — the four-phase strategy this roadmap operationalizes. - **ADR-009** — migration governance executed across 002b (baseline) and 002c (CI lane, CODEOWNERS). +- **ADR-012** — company cutover strategy; strategy-level decisions for the last, not-yet-authored + sub-plan (see assumption 8). +- **ADR-013** — capability-owned behavior over shared core entities; the placement principle that + governs whole-vs-split imports from 002d onward (see assumption 12). - **Plan 001** — device-messaging service extraction (parallel effort; see assumption 1). --- @@ -224,3 +269,49 @@ cutover. Weigh each rename individually; record all of them. execution-model note in each sub-plan header): per task, the maintainer chooses who executes; the agent must ask when unstated, verify actual repo state rather than assume, and always keep the bookkeeping regardless of who did the work. +- 2026-07-10 — Discussed company cutover strategy ahead of authoring the last sub-plan (its + prerequisites are far from complete). Surfaced runtime facts not considered in ADR-008: the 4 + legacy apps collapse to 2 hosts (`api`/`worker`, ADR-004/005), and the database is the primary + integration point for 5 frontends + Grafana + Make.com, not just the backend. Recorded as + **ADR-012**: schema convergence splits into anytime-safe vs. flip-atomic changes; `api`/`worker` + have different flip mechanics; RLS parity gets an explicit regression pass; hard point-of-no-return + past the flip-atomic migration (PITR checkpoint before); short maintenance window accepted; Geo + FastAPI out of scope. The ADR's illustrative cutover sequence is explicitly non-binding — the + authoritative runbook remains the just-in-time sub-plan, to reconcile with ADR-012 when authored. + New standing assumption 8 added; sub-plan index and related-documents updated to point to it. +- 2026-07-15 — **002d (Platform Core Import / Foundation) authored** after an extended maintainer + interview, and its Task 1 (decision records & registers) executed: **ADR-013** authored + (capability-owned behavior over shared core entities) + AGENTS.md ADR-index row; **ADR-004 §5** + amended (Foundation narrowed — agents/dcus/poles/websocket/routers/download out of always-on core) + and **ADR-007** amended (2026-07-15: `deployment` config group dropped; explicit central per-host + wiring supersedes decision 7's contribution functions; Foundation-wired hosts require DB / fail- + fast on `SUPABASE_*`). Created the **internationalization & de-brand register**; added the + "no-cracks" governance and standing assumption 12 (module-split → ADR-013); updated assumptions 2 + (ADR-005 = explicit 002e prerequisite) and 9 (deployment group dropped). Foundation noted as a + single-pass exception to the dual-pass import model. +- 2026-07-16 — **002d Task 4 done** (infra + explicit composition): Supabase provider, HTTP on both + hosts, Nest `Logger` + `GlobalLoggerModule` stub (nestjs-pino deferred), demo + `deployment` + config group removed, env rename (`SUPABASE_URL` / `SUPABASE_SECRET_KEY`). Next: Task 5 seed. +- 2026-07-16 — **002d Task 5 done** (seed harness): local `supabase/seed.sql` + + `docs/deployment/supabase.md` §5. Next: Task 6 (`accounts` + `api-keys`). +- 2026-07-17 — **002d Task 7 signed off** (auth): Passport strategies + guard, `AuthenticatedUser`, + JWKS/`jose`, inline API-key select, `/auth/me` + httpYac, ADR-014, CORS/`ValidationPipe`. + Seed verify (bearer + `X-API-KEY`) passed. Next: Task 8 (scoped test spike). +- 2026-07-17 — **002d Task 8 adopted** (test spike): `apps/api/test/` layout; unit default / + integration+e2e opt-in; ApiKeyStrategy + thin X-API-KEY e2e green. Next: Task 9. +- 2026-07-17 — **002d Task 8 signed off.** Next: Task 9 (`organizations` + `user-admin`). +- 2026-07-17 — **002d Task 9 signed off:** Nest `organizations` skipped; `user-admin` imported + (whole-method admin); `CreateCustomerDto` in `@nxt/core`; `handleSingle` + Cloudflare→503. + Near-future: ADR-014 §5.3 API-key → RLS-bound user client. Next was Task 10 (`grids`). +- 2026-07-17 — **002d Task 10 signed off:** Nest `grids` skipped (table/RLS/seed stay). Next: + Task 11 (close-out; grids = annotate service + remove `GET /:id` only). +- 2026-07-17 — **002d Completed** (Task 11 close-out): legacy Foundation deletes per ledger; + grids annotated + `GET /:id` removed; lint bar green; `demo`/`deployment` gone. Next was: + lock ADR-005, then author **002e**. +- 2026-07-17 — **ADR-005 Accepted** (inter-host communication). Independent hosts + shared DBs; + residual HTTP prefer worker→`api` (ADR-014); async per-capability DB jobs; retire `*_API` mesh; + no broker/`LISTEN` baseline; capability-owned ops writes; device-messaging = HTTP+callbacks. + Assumption 2 → Done. Next: author **002e** (Energy Production Monitoring). +- 2026-07-18 — **Branch strategy (assumption 6) resolved:** merge `oss-migration` → `main` after + Foundation (002d). Default development track is **`main`** going forward; `oss-migration` is + historical after merge (retarget any open work; tip optional to keep). README status blurb added. diff --git a/docs/plans/002-oss-migration/.gitignore b/docs/plans/002-oss-migration/.gitignore new file mode 100644 index 0000000..e17e2ff --- /dev/null +++ b/docs/plans/002-oss-migration/.gitignore @@ -0,0 +1,7 @@ +# Python artifacts from 002b generation/verification scripts (not part of any build) +__pycache__/ +*.py[cod] +*$py.class + +# Regenerated by parse_dump.py — do not commit (see 002b-task5-generation/README.md) +blocks.json diff --git a/docs/plans/002-oss-migration/002a-repo-restructure.md b/docs/plans/002-oss-migration/002a-repo-restructure.md index 3d09951..197a8f8 100644 --- a/docs/plans/002-oss-migration/002a-repo-restructure.md +++ b/docs/plans/002-oss-migration/002a-repo-restructure.md @@ -4,7 +4,7 @@ **Decisions:** ADR-008 (re-scaffold strategy); Step-0 mechanics decided with maintainer 2026-07-08 (see roadmap decisions log) **Created:** 2026-07-08 -**Status:** Not started +**Status:** Completed **Execution model:** collaborative — the maintainer may execute tasks manually with the agent advising, or the agent may execute under maintainer review. Ask which mode applies before starting a task; see "Division of labor" in the parent plan. @@ -44,6 +44,7 @@ Snapshot taken 2026-07-08. Verify against reality before executing (`ls -A`, `gi | `.husky/`, `.scripts/` | yes | → `legacy/` | | `.github/workflows/deploy-to-do.yml` | yes | → `legacy/.github-workflows/` (stub per ADR-009; moving it disables it — intended) | | `.gitignore`, `.github/` (rest, if any) | yes | stay at root | +| `.editorconfig` | yes | stay at root | | `README.md` | yes | → `legacy/` (a new root README is authored in a later phase — or this one is copied back and updated; `main` keeps the original meanwhile) | | `docs/`, `AGENTS.md`, `LICENSE`, `AUTHORS.md`, `CONTRIBUTING.md`, `CONTRIBUTORS.md` | yes | stay at root | | `.cursor/`, `.vscode/`, `.cursorignore` | yes | stay at root (editor/agent config; path-specific rules reviewed in 002c) | @@ -69,7 +70,7 @@ the later phases of the migration. ## Task 1 — The move commit (rename-only) -- [ ] **Status:** Not started +- [x] **Status:** Completed (2026-07-08) - **Depends on:** nothing Create the `oss-migration` branch from `main`. On a clean working tree, on that branch: @@ -103,7 +104,7 @@ returns only the intended root survivors (`docs/`, `.github/`, `.gitignore`, `.c ## Task 2 — Follow-up commit: freeze notice -- [ ] **Status:** Not started +- [x] **Status:** Completed (2026-07-08) - **Depends on:** Task 1 Content changes go in a **separate commit** after the move: @@ -128,7 +129,7 @@ phases of the migration (see "Accepted transitional state"). ## Task 3 — Local cleanup (per clone, not committed) -- [ ] **Status:** Not started +- [x] **Status:** Completed (2026-07-08) - **Depends on:** Task 1 1. Delete stale untracked artifacts at root: `dist/`, `.nx/`, `node_modules/`, `.DS_Store`, @@ -146,7 +147,7 @@ phases of the migration (see "Accepted transitional state"). ## Task 4 — Verification -- [ ] **Status:** Not started +- [x] **Status:** Completed (2026-07-08) - **Depends on:** Tasks 1–3 1. **History-follow spot check** on at least two deep files, e.g.: @@ -172,4 +173,9 @@ pushed. Update the roadmap sub-plan index: 002a → Completed; 002b and 002c unb > Append here as the plan is executed. Format: `YYYY-MM-DD — [task] — note` -_(empty)_ +- `2026-07-08 — [task 1] — Root `.editorconfig` exists in the live tree and remains at repo root; inventory table updated before the move.` +- `2026-07-08 — [task 1] — Move executed as a pure rename-only staged change set; local untracked Supabase artifacts were cleaned outside the move commit.` +- `2026-07-08 — [task 2] — Added `legacy/README.md` freeze notice and extended `.gitignore` to cover `legacy/supabase/.temp` and `.branches`, the only ignore rules broken by the move.` +- `2026-07-08 — [task 3] — Deleted stale root artifacts (`dist/`, `.nx/`, `.DS_Store`, `.snaplet/`); `node_modules/` was already absent and `core.hooksPath` was already unset in this clone.` +- `2026-07-08 — [task 4] — History-follow checks passed for `legacy/apps/tiamat/src/main.ts` and `legacy/libs/core/src/types/supabase-types.ts`.` +- `2026-07-08 — [task 4] — No root survivor outside `docs/` and `legacy/` referenced the old root paths except `.vscode/settings.json` search excludes for `libs/*/migration` and `supabase/migrations`; harmless for Step 0 and deferred to 002c path-specific cleanup.` diff --git a/docs/plans/002-oss-migration/002b-database-baseline.md b/docs/plans/002-oss-migration/002b-database-baseline.md index 60f4825..6862bc9 100644 --- a/docs/plans/002-oss-migration/002b-database-baseline.md +++ b/docs/plans/002-oss-migration/002b-database-baseline.md @@ -4,7 +4,7 @@ **Decisions:** ADR-004 (decision 3: migrations canonical, types derived; decision 9: schema stays whole), ADR-008 (Phase 2), ADR-009 (governance) **Created:** 2026-07-08 -**Status:** Not started +**Status:** Complete (2026-07-13) **Depends on:** 002a (repo restructure) complete — the old chain must live at `legacy/supabase/migrations` and the root must be clear. **Execution model:** collaborative — the maintainer may execute tasks manually with the agent @@ -26,7 +26,13 @@ the scaffold (Track B, 002c) and joins it at the interlock (types + CI drift gua **Companion artifacts (in this folder):** - `002b-schema-inventory.md` — working artifact: every schema object, classified (created in - Task 2/3). + Task 2/3a). +- `002b-schema-column-adjustments.md` — working artifact: every column on keep tables/views, + with keep/drop/rename action (**Task 3b** — complete). +- `002b-schema-programmability-review.md` — working artifact: enum values, functions, triggers + on keep objects (**Task 3c**). +- `002b-schema-performance-audit.md` — working artifact: indexes, function volatility, RLS + invocation patterns, trigger/view design on keep objects (**Task 3d**, stub created; not started). - `002b-schema-deviation-register.md` — long-lived register of every deviation from the original schema. **This register later becomes the spec for the company convergence migration at cutover.** Created up-front with candidate entries (see file). @@ -50,9 +56,6 @@ the scaffold (Track B, 002c) and joins it at the interlock (types + CI drift gua --local` → `gen-better-types` → `better-supabase-types` → `libs/core/src/types/supabase-types.ts`, plus `.scripts/fix-supabase-json-type.js`. Supabase CLI is a devDependency at `^2.54.10` (ADR-006 requires an exact pin — interim rule below). -- **Edge functions:** `legacy/supabase/functions/` (`customers`, `meter-consumption`, - `meter-consumption-2`, `orders`, `_shared`) — code, not schema; out of scope here (they are - re-homed during capability imports). - **Production project:** ref `axenumkepgnwfmdogkqq` (from the legacy `gen-types-remote` script). Production schema is assumed identical to the old chain (roadmap assumption 4 — certified in Task 1). @@ -60,7 +63,6 @@ the scaffold (Track B, 002c) and joins it at the interlock (types + CI drift gua ## Non-goals - **TimescaleDB** — belongs to the Production Monitoring capability import (roadmap assumption 3). -- **Edge functions** — re-homed during capability imports. - **RLS policy redesign** — policies are carried over as-is (or excluded with their tables); rethinking authorization is not baseline work. - **Migration-apply governance workflow, `squawk` linter, `db diff` PR comment** — deferred @@ -80,7 +82,7 @@ dumps compare apples to apples. ## Task 1 — Certify "production = migrations" (drift check) -- [ ] **Status:** Not started +- [x] **Status:** Complete - **Depends on:** 002a complete - **Executor:** maintainer (requires production credentials) @@ -107,7 +109,7 @@ input to Task 3) before proceeding. ## Task 2 — Build the reference DB and generate the schema inventory -- [ ] **Status:** Not started +- [x] **Status:** Complete - **Depends on:** 002a complete (Task 1 may run in parallel) Start the legacy chain locally and enumerate everything it creates: @@ -142,7 +144,7 @@ Useful starting queries: `information_schema.tables`, `pg_type` (enums), `pg_pro ## Task 3 — Four-bucket classification (with maintainer) -- [ ] **Status:** Not started +- [x] **Status:** Complete (2026-07-10) — 3a–3d signed off - **Depends on:** Task 2 Classify every inventory row into the ADR-008 buckets, in review sessions with the maintainer: @@ -163,15 +165,127 @@ Every non-keep decision and every rename becomes an entry in register file already contains candidate entries from the ADRs — confirm, amend, or reject each one during classification. -**Done when:** no inventory row has an empty bucket; every keep row has a capability tag; -the deviation register contains every non-keep decision and rename, each with a cutover -implication; maintainer has signed off. +### Task 3a — Object-level batches + +Review inventory rows in grouped batches (tables, enums, roles, …). Record bucket + +capability tag per row. Column drops discovered here are **provisional** until Task 3b. + +**Progress:** batches A–D, F1–F6 confirmed. **Task 3a complete** — every inventory row bucketed. + +### Task 3b — Column review pass (maintainer, after 3a) + +- [x] **Status:** Complete (2026-07-09) + +**Purpose:** walk **every column on every keep table** (and keep views) and confirm the final +set of column-level changes before Task 5. + +**Companion artifact:** `002b-schema-column-adjustments.md` — one row per column on keep +tables/views: + +`table | column | action (keep / drop / rename) | register § | rationale | notes` + +- Seed the file from the reference DB (`information_schema.columns` on keep tables), marking + known drops from Task 3a batches and the register **Column adjustments** section. +- Maintainer reviews table-by-table: add drops, remove drops, flag renames. +- Each **drop** or **rename** must land in the deviation register **Column adjustments** + (with cutover implication). + +**Done when:** `002b-schema-column-adjustments.md` has a final action for every column on every +keep table/view; all confirmed drops/renames are in the deviation register **Column adjustments** +(§1–§10); maintainer signed off batches G1–G4. + +**Result:** 587 columns — 535 keep, 49 drop, 3 rename (register #17–#20 + §1–§10). Full migration +chain used as source of truth (repaired after G2). + +### Task 3c — Programmability review (maintainer, after 3b) + +- [x] **Status:** **Complete** — H1–H5 signed off 2026-07-09 +- **Depends on:** Task 3b complete + +**Purpose:** sanity-check **keep** enums (per-value), functions, and triggers before Task 5. Object +bucket decisions from Task 3a are not re-litigated unless review surfaces a miss; this pass +confirms value-level trims, function/trigger renames already in the register, orphans, and +architectural fit (e.g. company-specific RLS helpers). + +**Companion artifact:** `002b-schema-programmability-review.md` — one row per enum value / +function / trigger on keep objects: + +- enum: `enum | value | action (keep / drop) | register § | rationale | notes` +- function: `function | action (keep / drop / rename) | register § | rationale | notes` +- trigger: `trigger | on table | action | register § | rationale | notes` + +**Seed from:** reference DB (`pg_enum`, `pg_proc`, `pg_trigger`) + inventory keep rows; pre-fill +register **#10** / **#16** function and trigger renames. + +**Review batches (suggested):** H1 RLS helpers → H2 auth/platform RPCs → H3 payments RPCs → H4 enum +value trim (by capability) → H5 triggers vs functions. + +**High-attention (not pre-decided):** `rls_check_if_nxt_member()` (NXT-specific RLS — may need +broader architecture discussion), `rls_check_if_lender()`, orphan +`append_rls_organization_id_by_historical_grid_id()`, deferred enum trims (`external_system_enum`, +`notification_type_enum`, customer enums, …). See review backlog in companion file. + +Each **drop**, **rename**, or enum **value drop** must land in the deviation register before Task 3 +is complete (add **Programmability adjustments** section as needed). + +**Done when (Task 3 overall):** Task 3c signed off; programmability artifact complete; register +contains every programmability deviation with cutover implication; maintainer signed off. + +### Task 3d — Database-wide performance audit (new, after 3a–3c) + +- [x] **Status:** **Complete 2026-07-10** — D1–D4 all signed off (registers #30, #31, #32; D4 had no findings, no register entry) +- **Depends on:** Task 3a, 3b, 3c complete (runs once the full keep-object list — tables, columns, + enums, functions, triggers, renames — is final, so nothing gets audited twice) + +**Purpose:** surfaced during Task 3c H1c (function volatility) but broadened at the maintainer's +request into a full **structural performance audit** of the baseline schema — done now because +this is the point where every keep object is locked in, and before Task 5 authors the init migration +(so findings are inputs to that authoring, not a retrofit) and before Task 6's A/B diff (so any +performance-driven change is registered before the diff runs, avoiding unexplained hunks). + +**Scope (structural/static only — see boundary below):** + +- **Function volatility** (`VOLATILE` / `STABLE` / `IMMUTABLE`) — not something Supabase sets for + you; the legacy chain leaves every function at the implicit default `VOLATILE` (confirmed: no + `CREATE FUNCTION` in the chain declares `STABLE`/`IMMUTABLE`). Unmarked RLS-attached functions can + be re-evaluated once per row instead of once per statement. +- **RLS policy invocation pattern** — the legacy chain is inconsistent: some policies wrap helper + calls in a subquery (`( SELECT public.rls_check_if_nxt_member() AS … )`, plan-cacheable), others + call bare (`public.rls_check_if_nxt_member()`). Distinct from volatility; both matter. +- **Indexes on keep tables** — **new ground, not previously inventoried** (Task 2's inventory has no + "index" kind at all). 3d must start by enumerating indexes on keep tables from the reference DB, + then check: FK columns indexed, RLS-predicate columns indexed (e.g. `rls_organization_id`), + redundant/overlapping indexes, coverage after Task 3 renames (`meter_command_batches` etc.). One + instance already caught and fixed directly during H1b rather than waiting for this pass — see + register #25 (`idx_accounts_organization_id`) — because it surfaced while reviewing a specific + function group; 3d is the systematic sweep for the rest of the schema. +- **Trigger design overhead** — H1b `append_rls_*` group resolved directly during Task 3c rather + than deferred here: dead join removed, join logic consolidated onto 6 shared helper functions, + `search_path` hardened. See register #23 and programmability review H1b. 3d still covers + trigger/view design for objects **outside** that group. +- **View definitions** — e.g. `meters_with_account_and_statuses` (wide view, many joins). + +**Out of scope / boundary:** empirical, load-driven tuning (`EXPLAIN ANALYZE` under realistic data +volumes) is not possible yet — there is no data, and per this plan's non-goals, "Any data migration — +this plan ships schema only." That kind of tuning belongs to a later capability-import or +production-readiness pass, once real usage exists. `SECURITY DEFINER` search-path hardening is +already present on most functions in the chain — spot-check only, not a full pass. + +**Companion artifact:** new `002b-schema-performance-audit.md` (stub to be created alongside this +task's start) — one section per concern above; findings that change schema shape (new/dropped +indexes, volatility changes, policy rewrites) sync to the deviation register **Performance +adjustments** section (added when 3d starts). + +**Done when:** every keep function has a justified volatility marking; RLS policy invocation pattern +is consistent; index coverage for keep tables is reviewed and gaps recorded; all resulting schema +changes are in the deviation register with cutover implications; init migration (Task 5) authors the +schema with the audited shape from the start. --- ## Task 4 — Scaffold the fresh root `supabase/` project -- [ ] **Status:** Not started +- [x] **Status:** Complete (2026-07-10) - **Depends on:** 002a complete (can run in parallel with Tasks 1–3) ```bash @@ -180,7 +294,9 @@ npx supabase@ init ``` - Pick and record the pinned CLI version (decisions log + interim tooling rule above). -- Set the Postgres major version in the new `config.toml` to match the legacy one. +- Set the Postgres major version in the new `config.toml` to match the legacy one (**15** — + provisional, for Task 6's A/B diff only; see the PG version flip note under Task 6, which + moves this to **17** — the leading target — once Task 6 signs off). - Review generated `config.toml` defaults; keep it minimal (no edge-function config yet). **Done when:** root `supabase/` exists with `config.toml` and an empty `migrations/`; @@ -190,17 +306,38 @@ npx supabase@ init ## Task 5 — Author the init migration -- [ ] **Status:** Not started +- [x] **Status:** Complete (2026-07-10) - **Depends on:** Tasks 3, 4 Create the single canonical migration, e.g. `supabase/migrations/_init.sql`: - **Content = keep bucket only**, with renames applied. Practical route: dump the reference DB (`pg_dump --schema-only`, owned schemas), then *edit down* per the classification — remove - drop/parameterize/exclude objects, apply renames, strip dump noise (ownership `ALTER`s, - supabase-managed grants, `SET` chatter). The result must read as an **authored document**, - not a dump: ordered (extensions → types → tables → constraints → functions → triggers → - policies → grants), commented per section. + drop/parameterize/exclude objects, apply renames, strip dump noise (ownership `ALTER`s, `SET` + chatter). The result must read as an **authored document**, not a dump: ordered (extensions → + types → tables → constraints → functions → triggers → policies → grants), commented per section. +- **Data API grants (register #33) — keep, do not strip as "noise":** carry forward + `GRANT ALL ON TABLE/SEQUENCE/FUNCTION … TO "anon"/"authenticated"/"service_role"` for every keep + object (Supabase stopped auto-granting these on new projects from 2026-05-30 — without them the + baseline is not reachable via the Data API at all). **Omit** the 3 + `ALTER DEFAULT PRIVILEGES … GRANT ALL ON TABLES/SEQUENCES/FUNCTIONS TO …` statements — future + tables (002c capability imports, adopter migrations) get explicit grants per migration instead of + an ambient auto-expose default. See register #33 / Data API access adjustments §1. +- **Extensions (register #11):** init migration runs `CREATE EXTENSION IF NOT EXISTS` only for + extensions **not** enabled on a fresh Supabase Postgres image (`postgis`, `pg_net`, `pgsodium`). + Omit platform defaults (`pg_stat_statements`, `pgcrypto`, `supabase_vault`, `uuid-ossp`), + `pg_graphql`, advisor extensions (`hypopg`, `index_advisor`), and `pgjwt` (not available on + Postgres 17 at all; confirmed unused in the legacy schema/app code — amended 2026-07-10). +- **FK cycle hardening (register #34):** the 5 denormalized "latest pointer" FKs + (`meters.last_metering_hardware_install_session_id`, + `metering_hardware_install_sessions.last_metering_hardware_import_id`, + `metering_hardware_install_sessions.last_meter_commissioning_id`, + `dcus.last_metering_hardware_install_session_id`, `meters.last_encountered_issue_id`) get + `ON DELETE SET NULL` in the init migration (legacy chain leaves them `NO ACTION`). Their 5 + structural back-pointer counterparts (`metering_hardware_install_sessions.meter_id`/`dcu_id`, + `metering_hardware_imports.metering_hardware_install_session_id`, + `meter_commissionings.metering_hardware_install_session_id`, `issues.meter_id`) stay `NO ACTION` — + unchanged. See register #34 / FK design adjustments §1. - No company roles, no supabase-managed schema objects, no deprecated/dead objects. - Objects referencing excluded objects (e.g. `orders` FKs to `directives`) are adjusted per the classification decisions — each adjustment is already in the register from Task 3. @@ -212,7 +349,7 @@ local DB with zero errors. ## Task 6 — A/B equivalence verification (the core gate) -- [ ] **Status:** Not started +- [x] **Status:** Complete (2026-07-13) - **Depends on:** Task 5 Compare old chain vs new baseline **sequentially** (avoids running two local stacks): @@ -235,11 +372,23 @@ Iterate Tasks 5↔6 until the assertion holds. diff hunk is annotated with its register entry number (keep the annotated diff as an appendix in the register file). +**PG version flip (after sign-off):** Task 4 set `config.toml`'s `major_version` to **15** +provisionally, matching legacy, so this diff isn't contaminated by PG-version-driven textual +noise on top of the real deviation-vs-register check. Postgres 17 is the leading target for the +OSS baseline going forward (Supabase's current default for all new projects, platform and +self-hosted, as of 2026 — decided in Task 4 discussion, 2026-07-10). Once this task's assertion +holds: flip `config.toml`'s `major_version` to **17** and re-run `npx supabase@ db +reset` against a clean local PG17 stack to confirm the init migration still applies with zero +errors — the same kind of check that caught the `pgjwt` incompatibility (register #11) before +it became a live blocker; there may be others. **17** is the value that ships from this point +forward. Task 9.2's fresh platform project (provisioned on PG17 by default) is the live +platform-side confirmation. Record the re-verification result in the decisions log. + --- ## Task 7 — Prove type generation against the baseline -- [ ] **Status:** Not started +- [x] **Status:** Complete (2026-07-13) - **Depends on:** Task 6 With the root stack running on the baseline: @@ -263,50 +412,92 @@ decisions log. ## Task 8 — Local bootstrap + seed decision -- [ ] **Status:** Not started +- [x] **Status:** Complete (2026-07-13) - **Depends on:** Task 6 -1. **Bootstrap proof:** from a clean clone of the branch, repo root: - `npx supabase@ start` → baseline applies → done. This is deployment consumer 1 - (roadmap) and the ADR-004 bootstrap flow minus types (002c completes it). -2. **Seed decision (with maintainer):** the ADR-007 config references DB rows - (`deployment.adminOrganizationId`, `deployment.systemWalletId`). Decide the minimal - `supabase/seed.sql` for a usable empty deployment (e.g. one organization row) — or decide - that bootstrap-SQL is documentation, not seed. Record the decision; implement if agreed. +1. **Bootstrap proof:** clean-clone path — `npx supabase@2.109.1 start` from repo root → init + migration applies with zero errors → full stack healthy (after Docker image/volume reset when + local health checks flaked). +2. **Seed decision:** **documentation-only** — no `supabase/seed.sql`. Post-migration bootstrap: + `docs/deployment/supabase.md` §5 + `supabase/snippets/post-migration-bootstrap-platform-operator.sql` + (platform operator org + org wallet). `BANKING_SYSTEM` / `deployment.systemWalletId` deferred to + payments capability setup. -**Done when:** clean-clone bootstrap works; seed decision recorded (and implemented if agreed). +**Done when:** clean-clone bootstrap works; seed decision recorded (and implemented if agreed). ✓ --- ## Task 9 — Staged rollout -- [ ] **Status:** Not started +- [x] **Status:** Complete (2026-07-13) - **Depends on:** Task 8 - **Executor:** maintainer for 9.2/9.3 (platform access) 1. **Local** — done via Task 8. 2. **Fresh Supabase platform project:** maintainer creates a new (non-production) project; `npx supabase@ link --project-ref ` from repo root; `db push` applies the - baseline. Verify in the dashboard (tables, types, no errors). This proves the cloud path - and is deployment consumer 2. -3. **Adopter instance:** gated on adopter coordination — apply the same procedure to the - adopter's Supabase project when they are ready. May happen later than this plan's - completion; leave the checkbox open until it does. Application is operator-controlled and - deliberate (ADR-009) — never wired to CI. + baseline. Verify in the dashboard (tables, types, no errors) **and** verify a keep table is + actually reachable via the Data API (e.g. a `supabase-js` `select` call or `curl /rest/v1/...`) + — a passing dashboard view alone does not prove Data API grants are correct (register #33: new + projects no longer auto-expose `public` tables as of 2026-05-30). This proves the cloud path + and is deployment consumer 2. ✓ +3. **Adopter instance:** same procedure on the adopter's Supabase project — operator-controlled + and deliberate (ADR-009), never wired to CI. ✓ -**Done when:** 9.2 verified clean; 9.3 done or explicitly parked with a note. +**Done when:** 9.2 verified clean; 9.3 done or explicitly parked with a note. ✓ --- ## Task 10 — Close out -- [ ] **Status:** Not started -- **Depends on:** Tasks 1–9 (9.3 may be parked) +- [x] **Status:** Complete (2026-07-13) +- **Depends on:** Tasks 1–9 + +- [x] Deviation register: final read-through — entries **#1–#34** all `confirmed` with cutover + implications; §pending backlogs empty (Task 10.1, 2026-07-13). +- [x] Roadmap: sub-plan index 002b → **Completed**; interlock deliverables recorded below and in + parent plan `docs/plans/002-oss-migration.md`. +- [x] Deferred follow-ups from 002b routed to tackle points (see **Deferred follow-ups** below). +- [x] Decisions log below: complete. + +### 002c interlock deliverables (handoff) + +Track A is done; Track B (**002c** Task 8) adopts: + +| Deliverable | Location / invocation | +|---|---| +| Canonical init migration | `supabase/migrations/20260710120000_init.sql` | +| Postgres major version | **17** (`supabase/config.toml` `db.major_version`) | +| Pinned Supabase CLI | **`2.109.1`** (`npx supabase@2.109.1 …` until 002c workspace pin) | +| Type generation | `npx supabase@2.109.1 gen types typescript --local --schema public` | +| Deviation register (cutover spec) | `002b-schema-deviation-register.md` | +| Deployment + bootstrap | `docs/deployment/supabase.md` | +| Generation pipeline (repro) | `docs/plans/002-oss-migration/002b-task5-generation/` | +| A/B compare tool | `docs/plans/002-oss-migration/002b-task6/compare_schemas.py` | -- Deviation register: final read-through; every entry has a cutover implication. -- Roadmap: sub-plan index 002b → Completed; note the interlock deliverables for 002c (baseline - migrations at root `supabase/`, pinned CLI version, gen-types invocation). -- Decisions log below: complete. +--- + +## Pre-Task-5 checklist (closed) + +All items that blocked authoring the init migration are **done**. Kept here for the record. + +- [x] **Task 3b** — Complete (2026-07-09). `002b-schema-column-adjustments.md`: 587 columns; 49 drops; 3 renames; G1–G4 signed off; register §1–§10. +- [x] **Task 3c** — Complete (2026-07-09). H1–H5 signed off; `002b-schema-programmability-review.md` final. +- [x] **Task 3d** — Complete (2026-07-10). D1–D4 signed off — registers #30, #31, #32; `002b-schema-performance-audit.md` final. +- [x] **Task 6 (pre-check)** — Register #11 extension block verified via Task 5 `db reset` on PG15 (provisional), then PG17 flip post-Task-6. + +--- + +## Deferred follow-ups (out of 002b scope) + +Not blockers for Tasks 5–9. **Routed** so they are picked up at the tackle point — do not rely on +re-reading this sub-plan. + +| Follow-up | Routed to | Status | +|---|---|---| +| **DB-native admin org — app consumers** (register #22): backend `getConfig().deployment.adminOrganizationId` call sites + frontend apps (qilin/pegasus/eos/niffler/sphinx) | **ADR-007** Amendment "Open / deferred" + **Triggers**; **002c Task 3** (config skeleton must resolve or explicitly defer); parent roadmap assumption **#9** | Open | +| **ADR-004 capability map** — remove `device-data-sink` from (1) Production monitoring; register **#12** (`devices` / `device_types` / `device_logs` dropped) | **ADR-004** §5 amended 2026-07-13; **002e** authoring (import scope) — parent roadmap assumption **#10** | ADR-004 **done**; 002e open | +| **NXT Grid production PG15→17** — OSS baseline is PG17; company production still PG15; separate from 002b/002c | **ADR-012** Triggers; cutover sub-plan prerequisites when authored; parent roadmap assumption **#11** | Open | --- @@ -314,4 +505,165 @@ decisions log. > Append here as the plan is executed. Format: `YYYY-MM-DD — [task] — note` -_(empty)_ +- 2026-07-08 — [Task 1] — Linked to production (`axenumkepgnwfmdogkqq`). Canonical `npx supabase@2.54.10 db diff --linked --schema public` failed: shadow-DB init errors on storage-api image (`Migration optimize-existing-functions-again not found` — CLI 2.54.10 vs pulled Docker image mismatch). Ran manual equivalent instead: (1) `db dump --linked -s public` + `-s auth`, (2) applied all 19 legacy migrations to a local Supabase Postgres 15 container, (3) compared live production (`inspect db table-stats --linked`) vs migrations DB. **Result: no substantive drift.** 57/57 `public` tables match by name; 42 enums, 27 functions, 140 indexes, 123 RLS policies match; auth triggers `on_auth_user_created` + `on_auth_user_updated` present on both production and migrations-applied DB. Residual dump-format differences only (quoting, `CREATE OR REPLACE` vs `CREATE`, default rendering). Roadmap assumption 4 certified. +- 2026-07-08 — [Task 1 tooling] — Root cause of CLI shadow-DB failure: `supabase link` caches production service pins in `legacy/supabase/.temp/` (`storage-version` v1.64.0, `storage-migration` optimize-existing-functions-again) that CLI 2.54.10's bundled storage-api cannot satisfy. Workaround: `rm legacy/supabase/.temp/storage-migration storage-version` before `db diff`/`db start`. Confirmed: `npx supabase@2.54.10 db diff --linked` and `db start` work after cache clear; canonical diff output is 2 cosmetic function-body formatting hunks only. `npx supabase@2.109.1 db diff --linked` works without cache clear. Full `supabase start` still blocked by deleted edge function refs in `config.toml` (`meter-consumption-2`). Task 4 CLI pin candidate: ≥2.62.10 or 2.109.1. +- 2026-07-08 — [Task 2] — Reference DB via `npx supabase@2.54.10 db start` (local only, no link). Inventory written to `002b-schema-inventory.md`: 191 rows — 57 tables, 4 views, 42 enums, 27 functions, 27 public triggers, 2 auth.users triggers, 17 sequences (nextval-owned), 3 roles, 11 extensions, 0 storage buckets. Owned schema: `public` only. +- 2026-07-08 — [Task 3 batch A] — Parameterize: 3 roles + 6 Make grid hooks (9 inventory rows). Delivery = separate operator recipes under `docs/database/optional/` (not in `supabase/migrations/`). Grafana/make bundles include their RLS policies. Register #2, #3, #5, #6 confirmed. +- 2026-07-08 — [Task 3 batch B] — Exclude deprecated directive system (register #1 confirmed): tables `directives`/`lorawan_directives`, view `batch_commands`, 6 enums (incl. `directive_type` + `directive_special_status` after column drops on keep tables), triggers/seq. Keep `directive_batches`/`directive_batch_executions` (rename candidates). Column adjustments §1: drop `orders.directive_id`/`lorawan_directive_id`, `directive_batches.directive_type`, `meters.current_special_status`, view column. Column prune backlog started (§pending). +- 2026-07-08 — [Task 3 batch C] — Exclude deprecated meter credit transfers (register #7 confirmed): table `meter_credit_transfers`, enum `meter_credit_transfer_status_enum`, sequence `meter_credit_transfers_id_seq`, trigger, function `append_rls_organization_id_by_receiver_meter_id()`. Register #4 narrowed to drop `directive_watchdog_sessions` only (`一demo` has no schema). Column adjustments §2: drop `orders.meter_credit_transfer_id`. +- 2026-07-08 — [Task 3 batch D] — Drop dead schema (registers #4, #8, #9 confirmed): `directive_watchdog_sessions`, `features`/`member_feature`, `public.migrations` (+ sequences/indexes/policies). No archive at cutover. `energy_cabins` **keep** (1) Production monitoring — pegasus map layer; tagged early outside Batch F. +- 2026-07-08 — [Task 3 batch F1] — Platform core **keep** (inventory tagged): accounts, orgs, members, agents, api_keys, grids, poles, `dcus`, routers, `agents_with_account`, auth triggers/functions, RLS helpers, platform enums. Register **#11** confirmed (extension policy). Storage bucket inventory row = placeholder only. +- 2026-07-09 — [Task 3 register #10 amended] — DCU→gateway rename **withdrawn**; register #10 narrowed to router trigger typo only (`append_rls_organization_id_on_route_insert` → `…_on_router_insert`). Column adjustments §3 removed. +- 2026-07-09 — [Task 3 batch F2] — Production monitoring **keep**: `mppts`, `solcast_cache`, `energy_cabins`, enums, mppt/cabin triggers, `get_grid_status`. Register **#12** **drop**: device registry. Column adjustments §4: drop `meters.device_id`. +- 2026-07-09 — [Task 3 batch F4] — Payments **keep**: `banks`, `wallets`, `transactions`, `orders`, payment enums (except `payout_status_enum`), `find_*` revenue RPCs, `lock_next_order_and_wallets`, wallet/transaction triggers. Register **#13** **drop**: `payouts`, `bank_accounts`, `payout_status_enum`, `lock_next_order()` (+ sequences/policies). `currency_enum` stays under **(3) Payments** (ADR-004 capability tag). +- 2026-07-09 — [Task 3 batch F5] — Notifications **keep**: `notifications`, `notification_parameters`, `notification_status_enum`, `notification_type_enum` (enum value trim deferred). No drops. Grid/member notification toggle columns deferred to Task 3b. +- 2026-07-09 — [Task 3 batch F6] — Field ops **keep**: `issues`, `notes`, `audits`, `pd_sites`, `pd_site_submissions` + issue enums/triggers. Register **#14** **drop**: pd-hero workflow subgraph + `lock_next_pd_action()`. Register **#15** **drop**: `autopilot_executions` (deferred capability). Column adjustments §5: drop `pd_sites.pd_flow_id`. `append_rls_organization_id_by_customer_id()` tagged shared (no capability). +- 2026-07-09 — [Task 3 batch F3] — Metering **keep**: customers, connections, meter_interactions, hardware install/import, USSD, views, enums, triggers. Register **#16** **rename**: `directive_batches` → `meter_command_batches`, `directive_batch_executions` → `meter_command_batch_executions` (+ sequences, function, triggers; column §6: `directive_batch_id` → `meter_command_batch_id`). `communication_protocol_enum` keep all values; customer enums keep (value trim deferred). No metering drops. **Task 3a complete.** +- 2026-07-08 — [Task 3 plan] — Split Task 3 into **3a** (object batches), **3b** (column review), and **3c** (programmability: enums/functions/triggers). Column artifact: `002b-schema-column-adjustments.md`; programmability artifact: `002b-schema-programmability-review.md`. +- 2026-07-09 — [Task 3b start] — Generated `002b-schema-column-adjustments.md` from legacy migration SQL (589 columns: 576 keep, 12 drop pre-filled, 1 rename). Docker unavailable for `information_schema` cross-check. Review batches G1+ (platform core first). §pending + register §1–§6 pre-filled; sync to register on batch sign-off. +- 2026-07-09 — [Task 3b G1] — Platform core signed off. **12 new drops:** `organizations.phone/address/pd_hero_google_drive_folder_id`, `api_keys.is_locked`, `dcus.queue_buffer_length`, `grids.is_automatic_payout_generation_enabled/telegram_response_path_autopilot/are_all_dcus_online/are_all_dcus_under_high_load_threshold/meter_*_threshold_*/uses_dual_meter_setup`. **Keeps confirmed:** notification toggles, `members.subscribed_to_telegram_revenue_notifications`, `grids.feature_access_config`. Register **#17** + column §3, §5 (amended), §7, §15 added; §pending narrowed to watchdog columns. +- 2026-07-09 — [Task 3b G2] — Metering signed off. File regenerated from full migration chain (fixes stale `goldring_migration_id`/`process_meta`, adds `task_type`/`batch_execution_id`/`payload_data`/etc.). **15 new drops:** watchdog cols (§4b, register #4), `directive_batches.lock_session/execution_bucket`, `meter_commissionings.*_steps/lock_session`, `meters.power_down_count/power_down_count_updated_at/is_simulated/pulse_counter_kwh/pulse_counter_kwh_updated_at` (+ view mirrors). Register **#18** + column §4b, §8; §pending cleared. +- 2026-07-09 — [Task 3b G3] — Payments + production monitoring signed off. Pre-filled order drops confirmed (§1, §2). **2 new drops:** `wallets.goldring_migration_id`, `orders.external_system`. Register **#19** + column §9. +- 2026-07-09 — [Task 3b G4] — Notifications + field ops signed off. `pd_sites.pd_flow_id` confirmed (§5). **2 renames:** `issues.external_system` → `external_tracking_system`, `issues.external_reference` → `external_tracking_reference`. **4 drops:** `issues.estimated_lost_revenue`, `snoozed_until`, `mppt_id`, `grid_id`. Register **#20** + column §10. +- 2026-07-09 — [Task 3b close] — **Task 3b complete.** All keep table/view columns reviewed (G1–G4). Enum value trim deferred to new **Task 3c** (programmability review). Companion stub: `002b-schema-programmability-review.md`. +- 2026-07-09 — [Task 3c start] — Generated `002b-schema-programmability-review.md` from full migration chain: 179 enum values (31 keep types), 20 functions, 21 triggers (19 public + 2 auth). Batch order: H1 (RLS helpers, split H1a/H1b/H1c) → H2 (auth/platform RPCs) → H3 (payments RPCs) → H4 (enum trim) → H5 (triggers). +- 2026-07-09 — [Task 3c H1a] — Signed off: `rls_check_if_lender()` keep; `append_rls_organization_id_by_historical_grid_id()` **drop** (orphan, register #21); `append_rls_organization_id_by_directive_batch_id()` rename confirmed (register #16). H1b (`append_rls_*` + triggers) split into a one-function-per-turn queue (10 items); H1c (`rls_check_if_nxt_member`) deferred as a separate architecture discussion. +- 2026-07-09 — [Task 3c H1c] — **Architecture decision, not a simple keep/drop:** `rls_check_if_nxt_member()` hard-codes `nxt_org_id := 2`; RLS cannot read `getConfig().deployment.adminOrganizationId` (ADR-007). Signed off: admin organization becomes **DB-native** — new enum value `organization_type_enum.PLATFORM_OPERATOR`, partial unique index (at most one), sync trigger on `organizations` maintaining a Postgres GUC (`app.admin_organization_id`) for fast RLS reads; function renamed → `rls_check_if_admin_org_member()`, marked `STABLE`. Register **#22** + Programmability adjustments §3. Full rationale recorded in **ADR-007 Amendment (2026-07-09)** (supersedes part of decisions 1/10); how backend `getConfig()` consumers and frontend apps resolve the now-DB-native value is explicitly **left open** in the amendment for later review. +- 2026-07-09 — [Task 3c scope changes] — Two process adjustments from maintainer: (1) new **Task 3d — Programmability performance audit** added, scoped to `VOLATILE`/`STABLE`/`IMMUTABLE` function markings (surfaced by the H1c `STABLE` discussion; not a Supabase default, legacy chain leaves every function `VOLATILE`) — runs after Task 3c closes, before Task 5. (2) **H1b** (`append_rls_*` + their triggers) will be reviewed as **one group discussion**, not one-by-one per function — an explicit exception to discussing other functions individually. +- 2026-07-09 — [Task 3c H1b] — Signed off. Correctness check on all 10 `append_rls_*` functions found no logical bugs, but 3 structural issues fixed **now** rather than deferred to Task 3d (maintainer preference — fix as issues surface, matching how H1c's performance issue was handled): (1) dead join to `organizations` in `by_account_id()` removed; (2) second dead lookup in the `organization_id` branch of `by_customer_id_or_agent_id_or_connec()` removed; (3) `SET search_path TO ''` added to all 10 (9 were missing it) — addresses Supabase "Function Search Path Mutable" linter warning. Broadened the search_path audit beyond this group per maintainer request ("ANY function"): also fixed on `lock_next_order_and_wallets()` (register #24; only other keep function missing it). Traced join-depth/index-coverage concern raised earlier — **retracted**: every join in every function resolves via a primary-key lookup (walk from known child row up through FK values to parent PKs), already optimal; no missing index. Found a **different**, real gap while tracing: `accounts.organization_id` backs 2 RLS policies with no supporting index (unlike every other RLS-filtered org column in the schema) — added `idx_accounts_organization_id` (register #25). Main architectural question — "can the rls_ column/trigger pattern be simplified?" — answered: **keep the denormalize-via-trigger strategy** (correct trade for read-heavy RLS; STABLE alone wouldn't help a per-row-varying join at query time), but **consolidate the duplicated join logic**: 10 trigger functions now delegate to **6 new** shared helper functions (`rls_org_id_from_grid/customer/connection/agent/meter/dcu`), each `LANGUAGE sql STABLE` (inlining-eligible, so delegation costs nothing at runtime vs. inlining the join by hand). Register **#23** (redesign) + Programmability adjustments §4–§6. +- 2026-07-09 — [Task 3c H2] — Signed off (one function at a time). `handle_new_user()` **keep** — bare account row on auth INSERT; already hardened. `handle_update_user()` **keep** — syncs `organization_id` to `accounts` for RLS; `account_type`/`member_type` stay JWT-only. `get_grid_status()` **keep + redesign** (register **#26**) — drop `are_all_dcus_online` + `are_all_dcus_under_high_load_threshold` from return type (columns dropped in register #17; pegasus uses separate `dcus` query for gateway alerts); mark **`STABLE`**. Programmability adjustments §7. +- 2026-07-09 — [Task 3c H3] — Signed off (one function at a time). `lock_next_order_and_wallets()` **keep** — atomic `FOR UPDATE SKIP LOCKED` payment lock; search_path fix already in register **#24**; stays `VOLATILE`. `find_energy_topup_revenue()` **keep + `STABLE`** (register **#27**). `find_top_spenders()` **keep + `STABLE` + GROUP BY fix** (#27) — drop `meta_receiver_id` (meter) from `GROUP BY` so top-spender rankings aggregate at customer level. Programmability adjustments §8. +- 2026-07-09 — [Task 3c H4a] — Platform-core enum trim signed off. `account_type_enum`, `organization_type_enum`, `weather_type_enum` **keep all**. `member_type_enum` **keep all 10** (RBAC vocabulary). `external_system_enum` **keep 11, drop 3** — drop `JOTFORM` (pd-hero #14), `STEAMACO`, `ACREL`; **keep `JIRA`** (required by `issues.external_tracking_system`, same enum type, DB default). Register **#28** + Programmability adjustments §9. +- 2026-07-09 — [Task 3c H4b] — Metering enums signed off. **Keep all** — no drops (`communication_protocol_enum`, customer enums, all core metering status/type enums). +- 2026-07-09 — [Task 3c H4c/H4d/H4e] — **H4 closed.** H4c payments + H4e field ops/production monitoring: **keep all**. H4d notifications: **keep 15, drop `AUTO_PAYOUT_GENRATION_REPORT`** only (payouts #13). Total enum value drops in H4: 4 (`external_system_enum` ×3, `notification_type_enum` ×1). Registers **#28**, **#29**. +- 2026-07-09 — [Task 3c H5a] — `append_rls_*` INSERT triggers signed off. **Keep all 19** — wiring confirmed vs H1b; 3 renames unchanged (#10 router, #16 ×2 meter command batches). No new register entries (already in #10/#16). +- 2026-07-09 — [Task 3c H5b/H5c] — **H5 closed; Task 3c complete.** H5b: keep `on_auth_user_created` + `on_auth_user_updated`. H5c: **add** `sync_admin_organization_id_guc` on `organizations` (#22). +- 2026-07-09 — [Register #16 amended] — Target names corrected: `meter_task_*` → **`meter_command_*`** (`meter_command_batches`, `meter_command_batch_executions`, `meter_command_batch_id`, function/trigger/index renames). Rationale: align with "command batch" domain language vs generic "task". +- 2026-07-09 — [Task 3d broadened] — Maintainer: Task 3d should be a **database-wide** structural performance audit, not just function volatility — done at this point because Task 3a–3c lock in the full keep-object list before Task 5 authors the init migration and Task 6 diffs it. Scope now also covers: RLS policy invocation pattern (bare vs. subquery-wrapped helper calls — legacy chain is inconsistent), index coverage on keep tables (**gap found:** Task 2's inventory never captured indexes as objects at all — 3d starts by enumerating them from the reference DB), trigger design overhead, view definitions. Explicit boundary: empirical/load-driven tuning (`EXPLAIN ANALYZE` under real data volume) is out of reach until a capability import brings real usage — plan ships schema only, no data migration. New companion artifact stub: `002b-schema-performance-audit.md`. +- 2026-07-10 — [Task 3d D1 signed off] — Batch order proposed and accepted: D1 (indexes) → D2 (function volatility) → D3 (RLS invocation pattern) → D4 (remaining triggers/views). D1: Docker was reachable this session, so queried the reference DB live (`pg_indexes`, FK constraints, `pg_policies`) across the 35 keep tables (105 existing indexes) instead of parsing migration SQL. **Gap sweep found 47 missing indexes** — Tier 1 (3, direct analog of register #25: `grids.organization_id`, `metering_hardware_imports.rls_organization_id` — table had zero non-PK indexes, `meter_command_batch_executions.meter_command_batch_id`) + Tier 2 (44 plain FK-column hygiene across 22 tables, e.g. `audits` had 8 unindexed FKs and only a PK index). Maintainer signed off both tiers as-is — register **#30** + Performance adjustments §1. Reviewed and excluded: 2 write-only FK columns with no read path (`members.busy_commissioning_id`, `meters.rls_grid_id`); 2 `orders` partial/full index pairs confirmed intentional (hot-path optimization), not redundant. **Observations** (RLS `qual = true` on `meter_interactions`/`wallets`; zero policies on `notifications`) kept as documented reference in the audit doc only — not a register entry (no schema change, so not a deviation) per maintainer request. D2–D4 not started. +- 2026-07-10 — [Task 3d D2 signed off] — Cross-checked all 26 keep functions against Task 3c's volatility decisions; only 2 had none: `rls_check_if_lender()`, `rls_get_member_org_id()` — both single-statement `auth.jwt()` reads, no side effects, same shape as `rls_check_if_admin_org_member()` (register #22, already `STABLE`). `rls_get_member_org_id()` is the most-invoked RLS helper in the schema (~24 policies across ~20 keep tables). Maintainer signed off marking both `STABLE` — register **#31** + Performance adjustments §2. Everything else already decided in 3c or correctly left `VOLATILE` by default (write side effects, or trigger functions where volatility marking has no planner effect). D2 complete; D3 (RLS invocation pattern) next. +- 2026-07-10 — [Task 3d D3 signed off] — Parsed all 123 `CREATE POLICY` statements (single migration file, never later altered) for the 35 keep tables; found 69 calls to the 3 policy-attached RLS helper functions (`rls_check_if_admin_org_member()`, `rls_check_if_lender()`, `rls_get_member_org_id()`), of which 51 were already subquery-wrapped (`( SELECT public.fn() AS fn )` — the cacheable `InitPlan` pattern) and 18 were bare, every one of them on an `INSERT`/`WITH CHECK` or `UPDATE`/`USING` clause (zero on `SELECT` — those were all already wrapped). Maintainer signed off normalizing all 18 to the wrapped form — register **#32** + Performance adjustments §3. Pairs with D2 (`STABLE`), which is what makes the wrap's caching valid. D3 complete; D4 (remaining triggers/views) next. +- 2026-07-10 — [Task 3d D4 signed off — **Task 3d complete**] — Checked every keep trigger and keep view against the plan's design-overhead/index-coverage concerns. Triggers: all fall into 2 already-resolved groups — `append_rls_*` (18, Task 3c H1b/register #23) and `auth.users` sync (`handle_new_user`/`handle_update_user`, Task 3c H2); spot-checked the `auth.users` group's design since it's outside the flagged family — single-statement, and `handle_update_user()`'s lookup column (`accounts.supabase_id`) already has a unique index. No third trigger group exists. Views: all 3 keep views' (`agents_with_account`, `customers_with_account`, `meters_with_account_and_statuses`) join columns are already index-covered (PKs or D1-confirmed FK indexes); `customers_with_account`'s one correlated subquery is a legacy design choice on an indexed column, noted not changed (views carried over as-is, same posture as D1's RLS-content observations). **Zero findings — no register entry.** Task 3d closed: D1–D4 all signed off, `002b-schema-performance-audit.md` final. +- 2026-07-10 — [Final sanity wrap-up, item 1/4] — Checked whether `orders.directive_status` needed renaming to `delivery_status` (maintainer's recollection). No such DB column exists on `orders` (only `directive_id`, dropped per register #1) — traced to the actual source: an **API response field** literally named `directive_status` in `tiamat`'s `getOrderDetailsPublic()` (`orders.service.ts`), with a stale inline comment `/* delivery_status */` flagging an intended rename that was never done. Sourced from `meter_interactions.meter_interaction_status`, already correctly named. Out of scope for the DB baseline (app-layer response shape, not a schema object) — left alone at maintainer's direction, no register entry. +- 2026-07-10 — [Final sanity wrap-up, item 2/4] — Checked `pg_graphql` handling (register #11) against current Supabase behavior: confirmed via Supabase's changelog that `pg_graphql` stopped being enabled by default on **2026-05-18** (existing projects with 30+ days zero GraphQL usage were auto-disabled too — explains current production state). Register #11's decision to omit `pg_graphql` from the init migration is unaffected and correctly matches current platform reality; no change needed. +- 2026-07-10 — [Final sanity wrap-up, item 3/4 — **register #33 added**] — Found a real, previously-unaddressed gap while checking Supabase's recent Data API changes: as of **2026-05-30**, new Supabase projects no longer auto-grant `anon`/`authenticated`/`service_role` access to `public` tables (enforced on **all** existing projects — new tables only — from 2026-10-30); without an explicit `GRANT`, PostgREST returns `42501` before RLS is even evaluated. The legacy migration chain's dump contains exactly the grants now required explicitly (`GRANT ALL ON TABLE/SEQUENCE/FUNCTION … TO anon/authenticated/service_role` per object, plus 3 `ALTER DEFAULT PRIVILEGES` statements for future tables) — but Task 5's original wording ("strip … supabase-managed grants") risked stripping the very grants that are now load-bearing, not just dump noise. Resolved: **keep** explicit per-object grants for all keep tables/sequences/functions (makes the baseline self-contained, independent of project-creation toggles or hosting mode); **omit** the 3 `ALTER DEFAULT PRIVILEGES` auto-expose-future-tables statements (deliberate alignment with Supabase's now-recommended explicit-grant-per-migration pattern; costs nothing today since production's existing tables are grandfathered regardless — this only shapes the baseline template and future 002c capability-import migrations). Register **#33** + new **Data API access adjustments** §1. Task 5 amended (grants section); Task 9.2 done-when amended (verify Data API reachability on the fresh hosted project, not just dashboard-clean). Also fixed a stale note found in the same pass: register file's "§pending — Performance backlog" still said D4 "not yet started" — corrected to reflect D1–D4 complete. +- 2026-07-10 — [Final sanity wrap-up, item 4/4 — **register #34 added**] — Maintainer recalled "back-and-forth" FK pointers between `meters`/`metering_hardware_install_sessions`/`metering_hardware_imports`/`meter_commissionings` and suspected redundancy/deletion/query-cost issues; asked for a full sweep. Built the complete FK graph across all 35 keep tables and searched for cycles: found exactly **5 direct 2-cycles** (confirmed complete via an independent `last_`/`latest_` column-name sweep), **0 cycles of length 3+** — the 3 named plus 2 more of the same shape (`dcus` ↔ `metering_hardware_install_sessions`; `meters` ↔ `issues`). All 5 are the same pattern: a denormalized "latest child" pointer column paired with the child's structural back-reference to its parent. Traced app-code usage and confirmed **all 5 are legitimate and actively read** — not redundant: explicit intent comment in `dcus.service.ts` ("point at the latest dcu session, so it's easily retrievable"), heavy use in the `meters_with_account_and_statuses` view (chains 4 of the 5 in one query), PostgREST embeds in `meter-installs.service.ts`/`meter-uninstalls.service.ts`, and a TypeORM `@OneToOne`. Checked indexing: both sides of every cycle are already covered (forward pointers via pre-existing `UNIQUE` constraints; structural back-pointers via register #30/Task 3d D1) — the query-efficiency concern is already resolved by prior work. Checked deletion: all 10 FKs in these cycles default to `NO ACTION` (real gap, matches the "hard to delete" complaint), but a full `legacy/` codebase sweep found **zero** hard-deletes anywhere (every removal is a soft-delete or status transition) — not live-impacting today, a latent trap for future manual/ops deletes. Maintainer accepted the recommendation: add `ON DELETE SET NULL` to the 5 forward pointers only; leave the 5 structural back-pointers `NO ACTION`. Register **#34** + new **FK design adjustments** §1. Task 5 amended (FK hardening bullet). This closes the final sanity wrap-up (items 1–4 of 4). +- 2026-07-10 — [Task 4 CLI pin] — Confirmed **`2.109.1`** as the Task 4 pin (also the current + latest published stable release, checked live against the npm registry — nothing newer except + `2.110.0-beta.*` prereleases). Matches Task 1's "≥2.62.10 or 2.109.1" candidates and Task 1's + finding that 2.109.1 works without the `.temp` cache-clear workaround 2.54.10 needed. Per the + interim tooling rule, this is a single frozen version for the rest of 002b (Tasks 4–9), not + something to keep bumping — only revisited if a concrete blocker forces it, same as Task 1's + workaround was a logged exception, not routine maintenance. Feeds the 002c workspace pin. +- 2026-07-10 — [Task 4 discussion — register #11 amended, `pgjwt` dropped] — While discussing + Task 4's Postgres major-version pin, checked whether a fresh Supabase project today still + provisions on PG15 (the plan's working assumption): it does not — Supabase now defaults **all** + new projects (platform and self-hosted) to **Postgres 17** as of 2026; regular users cannot + pick PG15 at creation (version selector is gated behind an internal Supabase-staff-only flag). + Cross-checked register #11's extension list against PG17: `postgis`/`pg_net` remain compatible; + `pgsodium` is "pending deprecation" per Supabase but not removed; **`pgjwt` is not available on + PG17 at all** (removed from the image bundle alongside `timescaledb`/`plv8`/`plcoffee`/`plls`) — + our planned `CREATE EXTENSION IF NOT EXISTS "pgjwt"` would have failed outright on any PG17 + project (i.e. every new adopter project going forward, and Task 9.2's fresh project). Grepped + the full legacy migration chain + all app code for pgjwt's functions (`sign`/`verify`/ + `url_encode`/`url_decode`/`algorithm_sign`) — zero usage beyond the bare `CREATE EXTENSION` + line, matching Supabase's own guidance that it's normally safe to disable. **Register #11 + amended:** drop `pgjwt` from the init migration's extension list — no functional loss, removes + a cross-version landmine before Task 5. +- 2026-07-10 — [Task 4 discussion — PG version strategy] — Maintainer: **Postgres 17 is the + leading target** for the OSS baseline (matches Supabase's own new-project default; "no forks, + single-track" per ADR-004 argues for one baseline valid across whatever PG version an + operator's project happens to run, rather than picking 15 specifically). Resolved the tension + with Task 6's "apples to apples" A/B-diff requirement (Option A, chosen over running Task 6 + cross-version): Task 4's `config.toml` sets `major_version = 15` **provisionally**, matching + legacy, so Task 6's diff isn't contaminated by PG-version-driven textual noise on top of the + real deviation-vs-register check. Once Task 6 signs off, `config.toml` flips to `major_version + = 17` and the init migration is re-verified with a clean `db reset` on a local PG17 stack (same + kind of check that just caught the `pgjwt` incompatibility) — **17** ships from that point + forward; Task 9.2's fresh platform project (already PG17 by default) is the live confirmation. + Plan text amended: Task 4 bullet + new "PG version flip" note under Task 6's done-when. Also + surfaced and logged as an open follow-up: NXT Grid's own production PG15→17 upgrade is a real, + currently untracked, independent prerequisite for full cutover parity — not 002b/002c scope, + no committed Supabase timeline, but worth an ADR-012 trigger once clearer. +- 2026-07-10 — [Task 4 complete] — Maintainer-executed (agent guiding step by step, per roadmap + division-of-labor). `npx supabase@2.109.1 init` at repo root scaffolded `supabase/config.toml` + + empty `migrations/` (also updated root `.gitignore` with newer CLI's env-file ignore + patterns — harmless template side effect). Generated `config.toml` defaulted `db.major_version` + to **17** (confirms the Task 4 discussion finding live); set to **15** per the agreed PG version + strategy (provisional, for Task 6's A/B diff — flips to 17 after Task 6 signs off, see note + under Task 6). Reviewed remaining generated defaults against "keep it minimal": nothing to + strip — the newer CLI template has no `[functions.*]` block to omit in the first place (those + are only added by `supabase functions new`), and all other new sections (`db.migrations`, + `db.seed`, `storage.vector`, `auth.oauth_server`, `experimental.pgdelta`, etc.) are inert + platform-service scaffolding, not edge-function config. `npx supabase@2.109.1 start`: first + attempt failed — cold image pull hit Docker registry rate-limiting (`toomanyrequests`, ~5.5min + with retries), and the `analytics` (Logflare) + `vector` containers missed their health-check + window afterward, so the CLI tore the stack down. Diagnosed as image-pull/health-check + flakiness, not a config problem (container logs showed normal startup chatter, no fatal error; + `WARN: no files matched pattern: supabase/seed.sql` is the expected/harmless Task 8 seed-file + gap, unrelated to the failure). `stop` + retry succeeded immediately once images were cached — + local stack came up healthy (DB, auth, storage all reporting). **Done-when met:** root + `supabase/` exists with `config.toml` + empty `migrations/`; `start` boots an empty local stack + from the repo root. +- 2026-07-10 — [Task 5 complete] — Authored `supabase/migrations/20260710120000_init.sql` (3,178 lines) from the legacy reference dump edited down per the deviation register (#1–#34 and all adjustment sections). Generation pipeline archived at `docs/plans/002-oss-migration/002b-task5-generation/` (reproducible — re-run verified byte-for-byte identical). Maintainer ran `npx supabase@2.109.1 db reset` from repo root: **zero errors**, done-when met. Also during Task 5 prep: `legacy/supabase/config.toml` `project_id` set to `skyfox-legacy` (avoids Docker volume collision with root `nxt-backend` stack); stale `meter-consumption-2` edge-function ref disabled; analytics disabled for legacy local stack stability. +- 2026-07-13 — [Task 6 complete] — Re-ran A/B verification (PG15 provisional): dumps + `/tmp/schema-old.sql` (legacy chain) vs `/tmp/schema-new.sql` (baseline after `db reset`). + Comparison tool archived at `docs/plans/002-oss-migration/002b-task6/compare_schemas.py`. + **226 explained diffs**, 9 script false-positives (all resolved — see register appendix). + Annotated diff appendix written to `002b-schema-deviation-register.md`. Maintainer sign-off. + Also: disabled `[analytics] enabled = false` on root `config.toml` (see note below). +- 2026-07-13 — [Local Supabase startup reliability] — Observed **two distinct** local + `supabase start` failure modes, not one: (1) cold Docker image pull / rate-limiting + (Task 4 first attempt — resolved by retry once images cached); (2) `analytics` + + `vector` containers reporting **unhealthy** before the CLI's `health_timeout` (2m) + elapses — consistent across Jul 10 and Jul 13 sessions on the root stack. Disabling + `[analytics] enabled = false` skips both services (vector is coupled to analytics) + and root `start` succeeded immediately afterward; legacy stack already used the same + setting. This is a **pragmatic local-dev default**, not a proof that analytics is + always the root cause — a longer `db.health_timeout` may help if you want analytics + locally. Documented inline in `supabase/config.toml`. +- 2026-07-13 — [PG17 flip complete] — `supabase/config.toml` `db.major_version` flipped + **15 → 17**. PG15 Docker volume wiped (`npx supabase@2.109.1 stop --no-backup` — required; + PG15 data dir incompatible with PG17 server). Fresh PG17 stack: init migration applied with + **zero errors** on first `start` (same NOTICEs: pg_net skip, 2 identifier truncations). + Verified: `SHOW server_version` → **17.6**; `one_platform_operator_org` index live. +- 2026-07-13 — [Task 7 complete] — Type generation against PG17 baseline succeeded. + Invocation (record for 002c): `npx supabase@2.109.1 gen types typescript --local --schema public`. + Output: 3,297 lines. Spot-check: `meter_command_batches` / `meter_command_batch_executions` + present; `directive_*` / `payouts` / `devices` absent; `PLATFORM_OPERATOR` enum value and + `rls_check_if_admin_org_member` RPC present. Full legacy `better-supabase-types` pipeline + deferred to 002c per plan. +- 2026-07-13 — [Task 8 complete] — **Bootstrap proof:** maintainer-driven after Docker + image/volume reset + PC restart; `npx supabase@2.109.1 db reset` / `start` → init migration + zero errors, full stack healthy. **Seed decision:** documentation-only (no `seed.sql`) — + `docs/deployment/supabase.md` §4–5 + snippet + `supabase/snippets/post-migration-bootstrap-platform-operator.sql` (platform operator org + + org wallet via dashboard or SQL). `BANKING_SYSTEM` wallet / `deployment.systemWalletId` left + to payments capability setup. Maintainer verified bootstrap insert locally. +- 2026-07-13 — [Task 8 — register #22 amended] — Original H1c design used a GUC + (`app.admin_organization_id`) + `sync_admin_organization_id_guc` trigger (`ALTER DATABASE … + SET`). Task 8 bootstrap exposed two blockers on Supabase: (1) migrations run as non-superuser + `postgres` — trigger's `ALTER DATABASE` fails with `42501` for normal SQL-editor callers; (2) + `ALTER FUNCTION … OWNER TO supabase_admin` in the migration also fails (`must be able to SET + ROLE "supabase_admin"`). **Resolved:** drop GUC trigger/function; `rls_check_if_admin_org_member()` + now reads `organizations` directly (`PLATFORM_OPERATOR` row via `one_platform_operator_org` + index; `LANGUAGE sql STABLE`). Same per-statement evaluation with register #32 subquery wraps. + Register #22 + Programmability adjustments §3 + ADR-007 Amendment (2026-07-13) updated. + Generation pipeline sections synced. +- 2026-07-13 — [Task 9.2 complete] — Fresh hosted Supabase project: `link` + `db push` from repo + root (`npx supabase@2.109.1`) applied init migration cleanly. Dashboard spot-check passed + (keep tables present, drop/exclude absent). Bootstrap via dashboard UI per + `docs/deployment/supabase.md` §5 (org, wallet, auth user, account `organization_id`, member + row). Data API grant check: `curl` against `/rest/v1/organizations` with service-role + credentials → **HTTP 200** (register #33 confirmed on hosted PG17). Supabase dashboard API-keys + UI now surfaces **publishable** / **secret** keys by default; legacy **anon** / **service_role** + JWT keys remain under a **Legacy API Keys** tab — either pair works for REST smoke tests. +- 2026-07-13 — [Task 9.3 complete] — Adopter instance: same `link` → `db push` → dashboard + verify → UI bootstrap procedure as 9.2 (maintainer-executed). +- 2026-07-13 — [Task 10 complete] — Register final read-through signed off (#1–#34, all cutover + implications present). Parent roadmap 002b → Completed; 002c interlock deliverables recorded + (Task 10 handoff table). Deferred follow-ups re-routed to ADR-007 / ADR-004 / ADR-012 + parent + roadmap assumptions #9–#11 (no longer 002b-only checkboxes). +- 2026-07-13 — [Deferred follow-up — ADR-004 §5] — Capability map amended: `device-data-sink` + removed from (1) Energy Production Monitoring; register **#12** cross-reference added (002e must + not import device registry). Parent assumption **#10** updated. diff --git a/docs/plans/002-oss-migration/002b-schema-column-adjustments.md b/docs/plans/002-oss-migration/002b-schema-column-adjustments.md new file mode 100644 index 0000000..8146219 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-schema-column-adjustments.md @@ -0,0 +1,619 @@ +# Schema Column Adjustments (working review) + +**Companion to:** `002b-database-baseline.md` — **Task 3b** +**Status:** Complete (2026-07-09) — G1–G4 signed off; register §1–§10 synced. +**Generated:** 2026-07-09 — full migration chain (CREATE + ALTER); repaired after G2 sign-off. + +## Summary + +| action | count | +|--------|------:| +| keep | 535 | +| drop | 49 | +| rename | 3 | + +Pre-filled from deviation register **Column adjustments** §1–§10. Table/column names use **legacy** +names where register entries do (renames applied in Task 5 init migration). + +## Register + +| table | column | action | register § | rationale | notes | +|-------|--------|--------|------------|-----------|-------| +| accounts | id | keep | | | type: integer NOT NULL | +| accounts | created_at | keep | | | type: timestamp(3) with time | +| accounts | full_name | keep | | | type: character varying | +| accounts | email | keep | | | type: character varying | +| accounts | phone | keep | | | type: character varying | +| accounts | telegram_id | keep | | | type: character varying | +| accounts | telegram_link_token | keep | | | type: character varying | +| accounts | deleted_at | keep | | | type: timestamp(3) without time | +| accounts | supabase_id | keep | | | type: "uuid" | +| accounts | organization_id | keep | | | type: integer | +| agents | id | keep | | | type: integer NOT NULL | +| agents | created_at | keep | | | type: timestamp(3) with time | +| agents | grid_id | keep | | | type: integer | +| agents | account_id | keep | | | type: integer | +| agents | rls_organization_id | keep | | | type: integer | +| api_keys | id | keep | | | type: integer NOT NULL | +| api_keys | created_at | keep | | | type: timestamp(3) with time | +| api_keys | is_locked | drop | §7 | G1 baseline prune — unused in OSS | type: boolean DEFAULT false | +| api_keys | key | keep | | | type: character varying | +| api_keys | account_id | keep | | | type: integer | +| audits | id | keep | | | type: integer NOT NULL | +| audits | created_at | keep | | | type: timestamp(3) with time | +| audits | message | keep | | | type: character varying NOT | +| audits | from_fs_command | keep | | | type: boolean DEFAULT false | +| audits | author_id | keep | | | type: integer | +| audits | grid_id | keep | | | type: integer | +| audits | organization_id | keep | | | type: integer | +| audits | meter_id | keep | | | type: integer | +| audits | agent_id | keep | | | type: integer | +| audits | customer_id | keep | | | type: integer | +| audits | member_id | keep | | | type: integer | +| audits | connection_id | keep | | | type: integer | +| audits | dcu_id | keep | | | type: integer | +| banks | id | keep | | | type: integer NOT NULL | +| banks | created_at | keep | | | type: timestamp(3) with time | +| banks | name | keep | | | type: character varying NOT | +| banks | external_id | keep | | | type: character varying NOT | +| connection_requested_meters | id | keep | | | type: integer NOT NULL | +| connection_requested_meters | created_at | keep | | | type: timestamp(3) with time | +| connection_requested_meters | meter_type | keep | | | type: "public"."meter_type_enum" NOT NULL | +| connection_requested_meters | meter_phase | keep | | | type: "public"."meter_phase_enum" NOT NULL | +| connection_requested_meters | fee | keep | | | type: double precision DEFAULT | +| connection_requested_meters | deleted_at | keep | | | type: timestamp(3) without time | +| connection_requested_meters | connection_id | keep | | | type: integer NOT NULL | +| connection_requested_meters | rls_organization_id | keep | | | type: integer | +| connections | id | keep | | | type: integer NOT NULL | +| connections | created_at | keep | | | type: timestamp(3) with time | +| connections | deleted_at | keep | | | type: timestamp(3) without time | +| connections | document_type | keep | | | type: "public"."id_document_type_enum" DEFAULT 'PASSPORT'::"public"."id_document_type_enum" | +| connections | document_id | keep | | | type: character varying | +| connections | upload_uuid | keep | | | type: character varying | +| connections | external_system | keep | | | type: "public"."external_system_enum" DEFAULT 'EPICOLLECT'::"public"."external_system_enum" | +| connections | paid | keep | | | type: double precision DEFAULT | +| connections | currency | keep | | | type: "public"."currency_enum" NOT NULL | +| connections | women_impacted | keep | | | type: integer NOT NULL | +| connections | is_lifeline | keep | | | type: boolean | +| connections | is_public | keep | | | type: boolean NOT NULL | +| connections | is_commercial | keep | | | type: boolean NOT NULL | +| connections | is_residential | keep | | | type: boolean NOT NULL | +| connections | is_building_wired | keep | | | type: boolean DEFAULT false | +| connections | is_using_led_bulbs | keep | | | type: boolean DEFAULT false | +| connections | customer_id | keep | | | type: integer | +| connections | rls_organization_id | keep | | | type: integer | +| customers | id | keep | | | type: integer NOT NULL | +| customers | created_at | keep | | | type: timestamp(3) with time | +| customers | gender | keep | | | type: "public"."gender_enum" | +| customers | total_connection_fee | keep | | | type: double precision DEFAULT | +| customers | total_connection_paid | keep | | | type: double precision DEFAULT | +| customers | is_hidden_from_reporting | keep | | | type: boolean DEFAULT false | +| customers | lives_primarily_in_the_community | keep | | | type: boolean DEFAULT true | +| customers | latitude | keep | | | type: double precision | +| customers | longitude | keep | | | type: double precision | +| customers | generator_owned | keep | | | type: "public"."generator_type_enum" | +| customers | grid_id | keep | | | type: integer | +| customers | account_id | keep | | | type: integer | +| customers | rls_organization_id | keep | | | type: integer | +| dcus | id | keep | | | type: integer NOT NULL | +| dcus | created_at | keep | | | type: timestamp(3) with time | +| dcus | external_reference | keep | | | type: character varying NOT | +| dcus | queue_buffer_length | drop | §7 | G1 baseline prune — unused in OSS | type: integer DEFAULT 50 | +| dcus | external_system | keep | | | type: "public"."external_system_enum" NOT NULL | +| dcus | is_online | keep | | | type: boolean DEFAULT false | +| dcus | last_online_at | keep | | | type: timestamp(3) with time | +| dcus | is_online_updated_at | keep | | | type: timestamp(3) with time | +| dcus | communication_protocol | keep | | | type: "public"."communication_protocol_enum" | +| dcus | grid_id | keep | | | type: integer | +| dcus | last_metering_hardware_install_session_id | keep | | | type: integer | +| dcus | rls_organization_id | keep | | | type: integer | +| dcus | location_geom | keep | | | type: "extensions"."geometry"(Point,4326) | +| directive_batch_executions | id | keep | | | type: integer NOT NULL | +| directive_batch_executions | created_at | keep | | | type: timestamp(3) with time | +| directive_batch_executions | pending_count | keep | | | type: integer DEFAULT 0 | +| directive_batch_executions | processing_count | keep | | | type: integer DEFAULT 0 | +| directive_batch_executions | successful_count | keep | | | type: integer DEFAULT 0 | +| directive_batch_executions | failed_count | keep | | | type: integer DEFAULT 0 | +| directive_batch_executions | processed_count | keep | | | type: integer DEFAULT 0 | +| directive_batch_executions | total_count | keep | | | type: integer DEFAULT 0 | +| directive_batch_executions | directive_batch_id | rename | §6 | Align with renamed parent table | → meter_command_batch_id | +| directive_batch_executions | rls_organization_id | keep | | | type: integer | +| directive_batch_executions | completed_at | keep | | | type: (added) | +| directive_batch_executions | qualified_at | keep | | | type: (added) | +| directive_batches | id | keep | | | type: integer NOT NULL | +| directive_batches | created_at | keep | | | type: timestamp(3) with time | +| directive_batches | is_deleted | keep | | | type: boolean DEFAULT false | +| directive_batches | hour | keep | | | type: integer NOT NULL | +| directive_batches | minute | keep | | | type: integer NOT NULL | +| directive_batches | is_repeating | keep | | | type: boolean DEFAULT false | +| directive_batches | grid_id | keep | | | type: integer | +| directive_batches | directive_type | drop | §1 | Legacy; live code uses task_type + fs_command | type: "public"."directive_type_enum" | +| directive_batches | fs_command | keep | | | type: "public"."fs_command_type_enum" | +| directive_batches | lock_session | drop | §8 | G2 baseline prune — unused in OSS | G2 signed off | +| directive_batches | execution_bucket | drop | §8 | G2 baseline prune — unused in OSS | G2 signed off | +| directive_batches | author_id | keep | | | type: integer | +| directive_batches | updated_at | keep | | | type: timestamp(3) with time | +| directive_batches | rls_organization_id | keep | | | type: integer | +| directive_batches | task_type | keep | | | type: (added) | +| energy_cabins | id | keep | | | type: integer NOT NULL | +| energy_cabins | created_at | keep | | | type: timestamp(3) with time | +| energy_cabins | grid_id | keep | | | type: integer NOT NULL | +| energy_cabins | location_geom | keep | | | type: "extensions"."geometry"(Point,4326) NOT NULL | +| energy_cabins | rls_organization_id | keep | | | type: integer | +| grids | id | keep | | | type: integer NOT NULL | +| grids | created_at | keep | | | type: timestamp(3) with time | +| grids | deleted_at | keep | | | type: timestamp with time | +| grids | deployed_at | keep | | | type: timestamp(3) with time | +| grids | commissioned_at | keep | | | type: timestamp(3) with time | +| grids | name | keep | | | type: character varying NOT | +| grids | is_hps_on | keep | | | type: boolean DEFAULT false | +| grids | is_hps_on_updated_at | keep | | | type: timestamp(3) with time | +| grids | walkthrough_external_id | keep | | | type: character varying | +| grids | timezone | keep | | | type: character varying DEFAULT | +| grids | kwp | keep | | | type: double precision DEFAULT | +| grids | kwh | keep | | | type: double precision DEFAULT | +| grids | kwp_tariff | keep | | | type: double precision DEFAULT | +| grids | kwh_tariff | keep | | | type: double precision DEFAULT | +| grids | kwh_tariff_essential_service | keep | | | type: double precision DEFAULT | +| grids | kwh_tariff_full_service | keep | | | type: double precision DEFAULT | +| grids | current_weather | keep | | | type: "public"."weather_type_enum" | +| grids | generation_external_system | keep | | | type: "public"."external_system_enum" DEFAULT 'VICTRON'::"public"."external_system_enum" | +| grids | metering_external_system | keep | | | type: "public"."external_system_enum" DEFAULT 'CALIN'::"public"."external_system_enum" | +| grids | generation_external_site_id | keep | | | type: character varying | +| grids | generation_external_gateway_id | keep | | | type: character varying | +| grids | generation_gateway_last_seen_at | keep | | | type: timestamp(3) with time | +| grids | is_fs_on | keep | | | type: boolean DEFAULT false | +| grids | is_fs_on_updated_at | keep | | | type: timestamp(3) with time | +| grids | should_fs_be_on | keep | | | type: boolean DEFAULT false | +| grids | should_fs_be_on_updated_at | keep | | | type: timestamp(3) with time | +| grids | default_hps_connection_fee | keep | | | type: double precision DEFAULT | +| grids | default_fs_1_phase_connection_fee | keep | | | type: double precision DEFAULT | +| grids | default_fs_3_phase_connection_fee | keep | | | type: double precision DEFAULT | +| grids | monthly_rental | keep | | | type: double precision DEFAULT | +| grids | are_all_dcus_online | drop | §7 | G1 baseline prune — unused in OSS | type: boolean DEFAULT false | +| grids | are_all_dcus_under_high_load_threshold | drop | §7 | G1 baseline prune — unused in OSS | type: boolean DEFAULT true | +| grids | is_hidden_from_reporting | keep | | | type: boolean DEFAULT true | +| grids | is_three_phase_supported | keep | | | type: boolean DEFAULT false | +| grids | is_using_vsat | keep | | | type: boolean DEFAULT false | +| grids | is_using_mobile_network | keep | | | type: boolean DEFAULT false | +| grids | is_hps_on_threshold_kw | keep | | | type: double precision | +| grids | kwh_per_battery_module | keep | | | type: double precision | +| grids | identifier | keep | | | type: integer | +| grids | lifeline_connection_kwh_threshold | keep | | | type: integer DEFAULT 5 | +| grids | lifeline_connection_days_threshold | keep | | | type: integer DEFAULT 30 | +| grids | meter_consumption_issue_threshold_detection_days | drop | §7 | G1 baseline prune — unused in OSS | type: integer DEFAULT 30 | +| grids | meter_communication_issue_threshold_detection_days | drop | §7 | G1 baseline prune — unused in OSS | type: integer DEFAULT 7 | +| grids | uses_dual_meter_setup | drop | §7 | G1 baseline prune — unused in OSS | type: boolean DEFAULT false | +| grids | is_panel_cleaning_notification_enabled | keep | | | type: boolean DEFAULT false | +| grids | is_energised_notification_enabled | keep | | | type: boolean DEFAULT false | +| grids | is_fs_on_notification_enabled | keep | | | type: boolean DEFAULT false | +| grids | is_metering_hardware_online_notification_enabled | keep | | | type: boolean DEFAULT false | +| grids | is_tariff_change_notification_enabled | keep | | | type: boolean DEFAULT false | +| grids | is_upcoming_fs_control_rule_notification_enabled | keep | | | type: boolean DEFAULT false | +| grids | is_fs_control_rule_change_notification_enabled | keep | | | type: boolean DEFAULT false | +| grids | is_automatic_meter_install_enabled | keep | | | type: boolean DEFAULT false | +| grids | is_automatic_payout_generation_enabled | drop | §3 | Payouts module dropped (register #13) | type: boolean DEFAULT false | +| grids | is_automatic_energy_generation_data_sync_enabled | keep | | | type: boolean DEFAULT false | +| grids | is_automatic_meter_energy_consumption_data_sync_enabled | keep | | | type: boolean DEFAULT false | +| grids | is_dcu_connectivity_tracking_enabled | keep | | | type: boolean DEFAULT false | +| grids | is_router_connectivity_tracking_enabled | keep | | | type: boolean DEFAULT false | +| grids | is_cabin_meter_credit_depleting | keep | | | type: boolean DEFAULT false | +| grids | telegram_response_path_token | keep | | | type: character varying | +| grids | telegram_notification_channel_invite_link | keep | | | type: character varying | +| grids | telegram_response_path_autopilot | drop | §15 | Autopilot deferred (register #15) | type: character varying | +| grids | internal_telegram_group_chat_id | keep | | | type: character varying | +| grids | internal_telegram_group_thread_id | keep | | | type: character varying | +| grids | organization_id | keep | | | type: integer NOT NULL | +| grids | meter_commissioning_initial_credit_kwh | keep | | | type: real DEFAULT '0'::real | +| grids | is_generation_managed_by_nxt_grid | keep | | | type: boolean DEFAULT true | +| grids | location_geom | keep | | | type: "extensions"."geometry"(Point,4326) | +| grids | telegram_config | keep | | | type: "jsonb" DEFAULT '{}'::"jsonb" | +| grids | feature_access_config | keep | | | type: "jsonb" DEFAULT '{}'::"jsonb" | +| issues | id | keep | | | type: integer NOT NULL | +| issues | created_at | keep | | | type: timestamp(3) with time | +| issues | issue_type | keep | | | type: "public"."issue_type_enum" NOT NULL | +| issues | issue_status | keep | | | type: "public"."issue_status_enum" DEFAULT 'OPEN'::"public"."issue_status_enum" | +| issues | external_system | rename | §10 | Clarify external ticket tracking vs integration system | → external_tracking_system; G4 signed off | +| issues | snoozed_until | drop | §10 | G4 baseline prune — unused in OSS | type: timestamp with time zone; G4 signed off | +| issues | started_at | keep | | | type: timestamp(3) with time zone | +| issues | closed_at | keep | | | type: timestamp(3) with time zone | +| issues | estimated_lost_revenue | drop | §10 | G4 baseline prune — lost-revenue job inactive | type: double precision DEFAULT; G4 signed off | +| issues | external_reference | rename | §10 | Pair with external_tracking_system rename | → external_tracking_reference; G4 signed off | +| issues | meter_id | keep | | | type: integer | +| issues | mppt_id | drop | §10 | G4 baseline prune — unused in OSS | type: integer; G4 signed off | +| issues | grid_id | drop | §10 | G4 baseline prune — grid derivable from meter | type: integer; G4 signed off | +| issues | rls_organization_id | keep | | | type: integer | +| members | id | keep | | | type: integer NOT NULL | +| members | created_at | keep | | | type: timestamp(3) with time | +| members | subscribed_to_telegram_revenue_notifications | keep | | | type: boolean DEFAULT false | +| members | member_type | keep | | | type: "public"."member_type_enum" DEFAULT 'DEVELOPER'::"public"."member_type_enum" | +| members | account_id | keep | | | type: integer | +| members | busy_commissioning_id | keep | | | type: integer | +| members | training_level | keep | | | type: smallint DEFAULT '0'::smallint | +| members | rls_organization_id | keep | | | type: integer | +| members | hidden | keep | | | type: boolean DEFAULT false | +| meter_commissionings | id | keep | | | type: integer NOT NULL | +| meter_commissionings | created_at | keep | | | type: timestamp(3) with time | +| meter_commissionings | meter_commissioning_status | keep | | | type: "public"."meter_commissioning_status_enum" DEFAULT 'PROCESSING'::"public"."meter_commissioning_status_enum" | +| meter_commissionings | initialised_steps | drop | §8 | G2 baseline prune — step counters unused | G2 signed off | +| meter_commissionings | pending_steps | drop | §8 | G2 baseline prune — step counters unused | G2 signed off | +| meter_commissionings | processing_steps | drop | §8 | G2 baseline prune — step counters unused | G2 signed off | +| meter_commissionings | successful_steps | drop | §8 | G2 baseline prune — step counters unused | G2 signed off | +| meter_commissionings | failed_steps | drop | §8 | G2 baseline prune — step counters unused | G2 signed off | +| meter_commissionings | total_steps | drop | §8 | G2 baseline prune — step counters unused | G2 signed off | +| meter_commissionings | lock_session | drop | §8 | G2 baseline prune — unused in OSS | G2 signed off | +| meter_commissionings | metering_hardware_install_session_id | keep | | | type: integer | +| meter_commissionings | rls_organization_id | keep | | | type: integer | +| meter_interactions | id | keep | | | type: bigint NOT NULL | +| meter_interactions | created_at | keep | | | type: timestamp with time | +| meter_interactions | updated_at | keep | | | type: timestamp with time | +| meter_interactions | meter_id | keep | | | type: integer NOT NULL | +| meter_interactions | token | keep | | | type: "text" | +| meter_interactions | order_id | keep | | | type: integer | +| meter_interactions | transactive_kwh | keep | | | type: real | +| meter_interactions | target_power_limit | keep | | | type: integer | +| meter_interactions | result_value | keep | | | type: "jsonb" | +| meter_interactions | meter_interaction_type | keep | | | type: "public"."meter_interaction_type_enum" NOT NULL | +| meter_interactions | meter_interaction_status | keep | | | type: "public"."meter_interaction_status_enum" DEFAULT 'QUEUED'::"public"."meter_interaction_status_enum" | +| meter_interactions | batch_execution_id | keep | | | type: (added) | +| meter_interactions | meter_commissioning_id | keep | | | type: (added) | +| meter_interactions | delivery_failure_history | keep | | | type: (added) | +| meter_interactions | payload_data | keep | | | type: (added) | +| metering_hardware_imports | id | keep | | | type: integer NOT NULL | +| metering_hardware_imports | created_at | keep | | | type: timestamp(3) with time | +| metering_hardware_imports | metering_hardware_import_operation | keep | | | type: "public"."mhi_operation_enum" DEFAULT 'ADD'::"public"."mhi_operation_enum" | +| metering_hardware_imports | metering_hardware_import_status | keep | | | type: "public"."mhi_status_enum" DEFAULT 'PENDING'::"public"."mhi_status_enum" | +| metering_hardware_imports | metering_hardware_install_session_id | keep | | | type: integer | +| metering_hardware_imports | lock_session | keep | | | type: character varying | +| metering_hardware_imports | rls_organization_id | keep | | | type: integer | +| metering_hardware_install_sessions | id | keep | | | type: integer NOT NULL | +| metering_hardware_install_sessions | created_at | keep | | | type: timestamp(3) with time | +| metering_hardware_install_sessions | dcu_id | keep | | | type: integer | +| metering_hardware_install_sessions | meter_id | keep | | | type: integer | +| metering_hardware_install_sessions | author_id | keep | | | type: integer | +| metering_hardware_install_sessions | last_meter_commissioning_id | keep | | | type: integer | +| metering_hardware_install_sessions | last_metering_hardware_import_id | keep | | | type: integer | +| metering_hardware_install_sessions | rls_organization_id | keep | | | type: integer | +| meters | id | keep | | | type: integer NOT NULL | +| meters | created_at | keep | | | type: timestamp(3) with time | +| meters | external_reference | keep | | | type: character varying NOT | +| meters | deleted_at | keep | | | type: timestamp(3) without time | +| meters | balance | keep | | | type: double precision | +| meters | balance_updated_at | keep | | | type: timestamp(3) with time | +| meters | kwh_credit_available | keep | | | type: double precision | +| meters | kwh_credit_available_updated_at | keep | | | type: timestamp(3) with time | +| meters | last_non_zero_consumption_at | keep | | | type: timestamp(3) with time | +| meters | is_on | keep | | | type: boolean DEFAULT false | +| meters | should_be_on | keep | | | type: boolean DEFAULT false | +| meters | is_on_updated_at | keep | | | type: timestamp(3) with time | +| meters | should_be_on_updated_at | keep | | | type: timestamp(3) with time | +| meters | is_manual_mode_on | keep | | | type: boolean DEFAULT false | +| meters | is_manual_mode_on_updated_at | keep | | | type: timestamp(3) with time | +| meters | voltage | keep | | | type: double precision | +| meters | voltage_updated_at | keep | | | type: timestamp(3) with time | +| meters | power | keep | | | type: double precision | +| meters | power_updated_at | keep | | | type: timestamp(3) with time | +| meters | latitude | keep | | | type: double precision | +| meters | longitude | keep | | | type: double precision | +| meters | coord_accuracy | keep | | | type: double precision DEFAULT | +| meters | power_limit | keep | | | type: integer | +| meters | power_limit_updated_at | keep | | | type: timestamp(3) with time | +| meters | power_limit_should_be | keep | | | type: integer | +| meters | power_down_count | drop | §8 | G2 baseline prune — unused in OSS | G2 signed off | +| meters | power_down_count_updated_at | drop | §8 | G2 baseline prune — unused in OSS | G2 signed off | +| meters | power_limit_should_be_updated_at | keep | | | type: timestamp(3) with time | +| meters | is_starred | keep | | | type: boolean DEFAULT false | +| meters | external_system | keep | | | type: "public"."external_system_enum" NOT NULL | +| meters | meter_type | keep | | | type: "public"."meter_type_enum" DEFAULT 'HPS'::"public"."meter_type_enum" | +| meters | nickname | keep | | | type: character varying | +| meters | last_seen_at | keep | | | type: timestamp(3) with time | +| meters | issue_check_execution_session | keep | | | type: character varying | +| meters | issue_check_last_run_at | keep | | | type: timestamp(3) with time | +| meters | pole_id | keep | | | type: integer | +| meters | meter_phase | keep | | | type: "public"."meter_phase_enum" DEFAULT 'SINGLE_PHASE'::"public"."meter_phase_enum" | +| meters | last_metering_hardware_install_session_id | keep | | | type: integer | +| meters | kwh_tariff | keep | | | type: double precision | +| meters | watchdog_session | drop | §4b | Watchdog module dropped (register #4); no app usage | type: character varying | +| meters | watchdog_last_run_at | drop | §4b | Watchdog module dropped (register #4); no app usage | type: timestamp(3) with time | +| meters | version | keep | | | type: character varying | +| meters | is_simulated | drop | §8 | G2 baseline prune — unused in OSS | G2 signed off | +| meters | power_limit_hps_mode | keep | | | type: integer DEFAULT 200 | +| meters | current_special_status | drop | §1 | Shadow of deprecated directive status; issues table is canonical | type: "public"."directive_special_status_enum" | +| meters | communication_protocol | keep | | | type: "public"."communication_protocol_enum" DEFAULT 'CALIN_LORAWAN'::public.communication_protocol_enum | +| meters | is_cabin_meter | keep | | | type: boolean DEFAULT false | +| meters | last_encountered_issue_id | keep | | | type: integer | +| meters | connection_id | keep | | | type: integer | +| meters | dcu_id | keep | | | type: integer | +| meters | decoder_key | keep | | | type: "text" | +| meters | last_sts_token_issued_at | keep | | | type: timestamp with time | +| meters | rls_grid_id | keep | | | type: integer | +| meters | rls_organization_id | keep | | | type: integer | +| meters | is_test_mode_on | keep | | | type: boolean DEFAULT false | +| meters | pulse_counter_kwh | drop | §8 | G2 baseline prune — unused in OSS | G2 signed off | +| meters | device_id | drop | §4 | Only referenced dropped devices table | type: bigint | +| meters | pulse_counter_kwh_updated_at | drop | §8 | G2 baseline prune — unused in OSS | G2 signed off | +| meters | connection_metrics | keep | | | type: "jsonb" | +| mppts | id | keep | | | type: integer NOT NULL | +| mppts | created_at | keep | | | type: timestamp(3) with time | +| mppts | external_reference | keep | | | type: character varying NOT | +| mppts | external_id | keep | | | type: character varying NOT | +| mppts | external_system | keep | | | type: "public"."external_system_enum" DEFAULT 'VICTRON'::"public"."external_system_enum" | +| mppts | kw | keep | | | type: double precision | +| mppts | azimuth | keep | | | type: double precision | +| mppts | tilt | keep | | | type: double precision | +| mppts | installed_at | keep | | | type: timestamp(3) with time | +| mppts | deleted_at | keep | | | type: timestamp(3) with time | +| mppts | mppt_type | keep | | | type: "public"."mppt_type_enum" DEFAULT 'MPPT'::"public"."mppt_type_enum" | +| mppts | grid_id | keep | | | type: integer | +| mppts | rls_organization_id | keep | | | type: integer | +| notes | id | keep | | | type: integer NOT NULL | +| notes | created_at | keep | | | type: timestamp(3) with time | +| notes | message | keep | | | type: character varying | +| notes | customer_id | keep | | | type: integer | +| notes | connection_id | keep | | | type: integer | +| notes | meter_id | keep | | | type: integer | +| notes | author_id | keep | | | type: integer | +| notes | rls_organization_id | keep | | | type: integer | +| notification_parameters | id | keep | | | type: integer NOT NULL | +| notification_parameters | created_at | keep | | | type: timestamp(3) with time | +| notification_parameters | parameters | keep | | | type: "json" | +| notifications | id | keep | | | type: integer NOT NULL | +| notifications | created_at | keep | | | type: timestamp(3) with time | +| notifications | connector_external_system | keep | | | type: "public"."external_system_enum" | +| notifications | carrier_external_system | keep | | | type: "public"."external_system_enum" | +| notifications | notification_type | keep | | | type: "public"."notification_type_enum" NOT NULL | +| notifications | notification_status | keep | | | type: "public"."notification_status_enum" NOT NULL | +| notifications | external_reference | keep | | | type: character varying | +| notifications | notification_parameter_id | keep | | | type: integer | +| notifications | grid_id | keep | | | type: integer | +| notifications | organization_id | keep | | | type: integer | +| notifications | account_id | keep | | | type: integer | +| notifications | lock_session | keep | | | type: character varying | +| notifications | message | keep | | | type: character varying | +| notifications | phone | keep | | | type: character varying | +| notifications | subject | keep | | | type: character varying | +| notifications | email | keep | | | type: character varying | +| notifications | chat_id | keep | | | type: character varying | +| notifications | thread_id | keep | | | type: character varying | +| orders | id | keep | | | type: integer NOT NULL | +| orders | created_at | keep | | | type: timestamp(3) with time | +| orders | amount | keep | | | type: double precision NOT | +| orders | order_status | keep | | | type: "public"."order_status_enum" DEFAULT 'PENDING'::"public"."order_status_enum" | +| orders | lock_session | keep | | | type: character varying | +| orders | external_reference | keep | | | type: character varying | +| orders | currency | keep | | | type: "public"."currency_enum" NOT NULL | +| orders | external_system | drop | §9 | G3 baseline prune — unused in OSS | type: "public"."external_system_enum"; G3 signed off | +| orders | tariff_type | keep | | | type: "public"."meter_type_enum" | +| orders | tariff | keep | | | type: double precision DEFAULT | +| orders | payment_method | keep | | | type: "public"."payment_method_enum" | +| orders | payment_channel | keep | | | type: "public"."payment_channel_enum" | +| orders | author_id | keep | | | type: integer | +| orders | directive_id | drop | §1 | Only referenced excluded directive tables | type: bigint | +| orders | meter_credit_transfer_id | drop | §2 | Only referenced excluded meter_credit_transfers | type: integer | +| orders | historical_grid_id | keep | | | type: integer | +| orders | meta_author_type | keep | | | type: "public"."account_type_enum" | +| orders | meta_author_name | keep | | | type: character varying | +| orders | meta_author_id | keep | | | type: integer | +| orders | meta_order_type | keep | | | type: "public"."order_type_enum" | +| orders | meta_sender_id | keep | | | type: integer | +| orders | meta_sender_name | keep | | | type: character varying | +| orders | meta_receiver_name | keep | | | type: character varying | +| orders | meta_receiver_name_part_2 | keep | | | type: character varying | +| orders | meta_sender_name_part_2 | keep | | | type: character varying | +| orders | meta_receiver_id | keep | | | type: integer | +| orders | meta_sender_type | keep | | | type: "public"."order_actor_type_enum" | +| orders | meta_receiver_type | keep | | | type: "public"."order_actor_type_enum" | +| orders | meta_is_hidden_from_reporting | keep | | | type: boolean | +| orders | sender_wallet_id | keep | | | type: integer | +| orders | receiver_wallet_id | keep | | | type: integer | +| orders | ussd_session_id | keep | | | type: integer | +| orders | lorawan_directive_id | drop | §1 | Only referenced excluded directive tables | type: bigint | +| orders | meta_receiver_id_part_2 | keep | | | type: integer | +| orders | rls_organization_id | keep | | | type: integer | +| orders | updated_at | keep | | | type: timestamp with time | +| organizations | id | keep | | | type: integer NOT NULL | +| organizations | created_at | keep | | | type: timestamp(3) with time | +| organizations | name | keep | | | type: character varying NOT | +| organizations | formal_name | keep | | | type: character varying | +| organizations | email | keep | | | type: character varying | +| organizations | phone | drop | §7 | G1 baseline prune — unused in OSS | type: character varying | +| organizations | address | drop | §7 | G1 baseline prune — unused in OSS | type: character varying | +| organizations | epicollect_contract_survey_slug | keep | | | type: character varying | +| organizations | epicollect_contract_survey_secret | keep | | | type: character varying | +| organizations | epicollect_contract_survey_client_id | keep | | | type: character varying | +| organizations | epicollect_contract_last_sync_at | keep | | | type: timestamp(3) with time | +| organizations | developer_group_telegram_chat_id | keep | | | type: character varying | +| organizations | deleted_at | keep | | | type: timestamp with time | +| organizations | pd_hero_google_drive_folder_id | drop | §5 | pd-hero workflow dropped (register #14) | type: "text" | +| organizations | organization_type | keep | | | type: "public"."organization_type_enum" DEFAULT 'SOLAR_DEVELOPER'::"public"."organization_type_enum" | +| pd_site_submissions | id | keep | | | type: bigint NOT NULL | +| pd_site_submissions | created_at | keep | | | type: timestamp with time | +| pd_site_submissions | author_full_name | keep | | | type: "text" NOT NULL | +| pd_site_submissions | author_email | keep | | | type: "text" NOT NULL | +| pd_site_submissions | author_organization_name | keep | | | type: "text" NOT NULL | +| pd_site_submissions | site_name | keep | | | type: character varying NOT | +| pd_site_submissions | site_details | keep | | | type: "jsonb" NOT NULL | +| pd_site_submissions | outline_geom | keep | | | type: "extensions"."geometry"(Polygon,4326) | +| pd_site_submissions | deleted_at | keep | | | type: timestamp with time | +| pd_site_submissions | location_geom | keep | | | type: "extensions"."geometry"(Point,4326) | +| pd_site_submissions | organization_id | keep | | | type: integer | +| pd_site_submissions | author_organization_id | keep | | | type: integer | +| pd_site_submissions | buildings_geo_flat | keep | | | type: "jsonb" | +| pd_site_submissions | distribution_geo_flat | keep | | | type: "jsonb" | +| pd_site_submissions | meta_geo_flat | keep | | | type: "jsonb" | +| pd_site_submissions | poles_geo_flat | keep | | | type: "jsonb" | +| pd_sites | id | keep | | | type: bigint NOT NULL | +| pd_sites | created_at | keep | | | type: timestamp with time | +| pd_sites | organization_id | keep | | | type: integer | +| pd_sites | name | keep | | | type: "text" NOT NULL | +| pd_sites | deleted_at | keep | | | type: timestamp with time | +| pd_sites | location_geom | keep | | | type: "extensions"."geometry"(Point,4326) | +| pd_sites | outline_geom | keep | | | type: "extensions"."geometry"(Polygon,4326) | +| pd_sites | pd_flow_id | drop | §5 | Only referenced dropped pd-hero workflow tables | type: bigint | +| pd_sites | operations_grid_id | keep | | | type: integer | +| poles | id | keep | | | type: integer NOT NULL | +| poles | created_at | keep | | | type: timestamp(3) with time | +| poles | external_reference | keep | | | type: character varying(10) NOT | +| poles | nickname | keep | | | type: character varying | +| poles | is_virtual | keep | | | type: boolean DEFAULT false | +| poles | location_accuracy | keep | | | type: double precision | +| poles | grid_id | keep | | | type: integer | +| poles | location_geom | keep | | | type: "extensions"."geometry"(Point,4326) NOT NULL | +| poles | rls_organization_id | keep | | | type: integer | +| routers | id | keep | | | type: integer NOT NULL | +| routers | created_at | keep | | | type: timestamp(3) with time | +| routers | external_reference | keep | | | type: character varying NOT | +| routers | external_system | keep | | | type: "public"."external_system_enum" DEFAULT 'ZEROTIER'::"public"."external_system_enum" | +| routers | is_online | keep | | | type: boolean DEFAULT false | +| routers | is_online_updated_at | keep | | | type: timestamp(3) with time | +| routers | deleted_at | keep | | | type: timestamp(3) with time | +| routers | grid_id | keep | | | type: integer | +| routers | rls_organization_id | keep | | | type: integer | +| solcast_cache | id | keep | | | type: integer NOT NULL | +| solcast_cache | created_at | keep | | | type: timestamp(3) with time | +| solcast_cache | request_type | keep | | | type: "public"."solcast_cache_request_type_enum" NOT NULL | +| solcast_cache | latitude | keep | | | type: numeric(8,6) NOT NULL | +| solcast_cache | longitude | keep | | | type: numeric(9,6) NOT NULL | +| solcast_cache | tilt | keep | | | type: numeric(10,3) NOT NULL | +| solcast_cache | azimuth | keep | | | type: numeric(10,3) NOT NULL | +| solcast_cache | capacity_kwp | keep | | | type: numeric(10,3) NOT NULL | +| solcast_cache | install_date | keep | | | type: character varying NOT | +| solcast_cache | response | keep | | | type: "text" NOT NULL | +| transactions | id | keep | | | type: bigint NOT NULL | +| transactions | created_at | keep | | | type: timestamp(3) with time | +| transactions | amount | keep | | | type: double precision NOT | +| transactions | transaction_status | keep | | | type: "public"."transaction_status_enum" NOT NULL | +| transactions | balance_before | keep | | | type: double precision DEFAULT | +| transactions | balance_after | keep | | | type: double precision DEFAULT | +| transactions | wallet_id | keep | | | type: integer | +| transactions | order_id | keep | | | type: integer | +| transactions | rls_organization_id | keep | | | type: integer | +| ussd_session_hops | id | keep | | | type: integer NOT NULL | +| ussd_session_hops | created_at | keep | | | type: timestamp(3) with time | +| ussd_session_hops | text | keep | | | type: character varying | +| ussd_session_hops | phone | keep | | | type: character varying | +| ussd_session_hops | network_code | keep | | | type: character varying | +| ussd_session_hops | service_code | keep | | | type: character varying | +| ussd_session_hops | ussd_session_id | keep | | | type: integer | +| ussd_sessions | id | keep | | | type: integer NOT NULL | +| ussd_sessions | created_at | keep | | | type: timestamp(3) with time | +| ussd_sessions | phone | keep | | | type: character varying NOT | +| ussd_sessions | amount | keep | | | type: double precision | +| ussd_sessions | external_reference | keep | | | type: character varying | +| ussd_sessions | external_system | keep | | | type: "public"."external_system_enum" NOT NULL | +| ussd_sessions | is_using_other_option | keep | | | type: boolean DEFAULT false | +| ussd_sessions | account_id | keep | | | type: integer | +| ussd_sessions | meter_id | keep | | | type: integer | +| ussd_sessions | bank_id | keep | | | type: integer | +| wallets | id | keep | | | type: integer NOT NULL | +| wallets | created_at | keep | | | type: timestamp(3) with time | +| wallets | organization_id | keep | | | type: integer | +| wallets | agent_id | keep | | | type: integer | +| wallets | customer_id | keep | | | type: integer | +| wallets | meter_id | keep | | | type: integer | +| wallets | lock_session | keep | | | type: character varying | +| wallets | balance | keep | | | type: double precision DEFAULT | +| wallets | balance_updated_at | keep | | | type: timestamp(3) with time | +| wallets | identifier | keep | | | type: character varying | +| wallets | wallet_type | keep | | | type: "public"."wallet_type_enum" DEFAULT 'REAL'::"public"."wallet_type_enum" | +| wallets | goldring_migration_id | drop | §9 | G3 baseline prune — Goldring migration-era ID | type: integer; G3 signed off | +| wallets | connection_id | keep | | | type: integer | +| wallets | rls_organization_id | keep | | | type: integer | +| agents_with_account | id | keep | | | view column | +| agents_with_account | created_at | keep | | | view column | +| agents_with_account | grid_id | keep | | | view column | +| agents_with_account | account_id | keep | | | view column | +| agents_with_account | full_name | keep | | | view column | +| agents_with_account | phone | keep | | | view column | +| agents_with_account | deleted_at | keep | | | view column | +| customers_with_account | id | keep | | | view column | +| customers_with_account | created_at | keep | | | view column | +| customers_with_account | gender | keep | | | view column | +| customers_with_account | total_connection_fee | keep | | | view column | +| customers_with_account | total_connection_paid | keep | | | view column | +| customers_with_account | is_hidden_from_reporting | keep | | | view column | +| customers_with_account | lives_primarily_in_the_community | keep | | | view column | +| customers_with_account | latitude | keep | | | view column | +| customers_with_account | longitude | keep | | | view column | +| customers_with_account | generator_owned | keep | | | view column | +| customers_with_account | grid_id | keep | | | view column | +| customers_with_account | account_id | keep | | | view column | +| customers_with_account | full_name | keep | | | view column | +| customers_with_account | phone | keep | | | view column | +| customers_with_account | deleted_at | keep | | | view column | +| customers_with_account | has_fully_paid_connection_fees | keep | | | view column | +| customers_with_account | meter | keep | | | view column — comma-separated meter external refs | +| meters_with_account_and_statuses | id | keep | | | view column | +| meters_with_account_and_statuses | created_at | keep | | | view column | +| meters_with_account_and_statuses | external_reference | keep | | | view column | +| meters_with_account_and_statuses | deleted_at | keep | | | view column | +| meters_with_account_and_statuses | balance | keep | | | view column | +| meters_with_account_and_statuses | balance_updated_at | keep | | | view column | +| meters_with_account_and_statuses | kwh_credit_available | keep | | | view column | +| meters_with_account_and_statuses | kwh_credit_available_updated_at | keep | | | view column | +| meters_with_account_and_statuses | last_non_zero_consumption_at | keep | | | view column | +| meters_with_account_and_statuses | is_on | keep | | | view column | +| meters_with_account_and_statuses | should_be_on | keep | | | view column | +| meters_with_account_and_statuses | is_on_updated_at | keep | | | view column | +| meters_with_account_and_statuses | should_be_on_updated_at | keep | | | view column | +| meters_with_account_and_statuses | is_manual_mode_on | keep | | | view column | +| meters_with_account_and_statuses | is_manual_mode_on_updated_at | keep | | | view column | +| meters_with_account_and_statuses | voltage | keep | | | view column | +| meters_with_account_and_statuses | voltage_updated_at | keep | | | view column | +| meters_with_account_and_statuses | power | keep | | | view column | +| meters_with_account_and_statuses | power_updated_at | keep | | | view column | +| meters_with_account_and_statuses | latitude | keep | | | view column | +| meters_with_account_and_statuses | longitude | keep | | | view column | +| meters_with_account_and_statuses | coord_accuracy | keep | | | view column | +| meters_with_account_and_statuses | power_limit | keep | | | view column | +| meters_with_account_and_statuses | power_limit_updated_at | keep | | | view column | +| meters_with_account_and_statuses | power_limit_should_be | keep | | | view column | +| meters_with_account_and_statuses | power_down_count | drop | §8 | Follows meters column drop | view column; G2 signed off | +| meters_with_account_and_statuses | power_down_count_updated_at | drop | §8 | Follows meters column drop | view column; G2 signed off | +| meters_with_account_and_statuses | power_limit_should_be_updated_at | keep | | | view column | +| meters_with_account_and_statuses | is_starred | keep | | | view column | +| meters_with_account_and_statuses | external_system | keep | | | view column | +| meters_with_account_and_statuses | meter_type | keep | | | view column | +| meters_with_account_and_statuses | nickname | keep | | | view column | +| meters_with_account_and_statuses | last_seen_at | keep | | | view column | +| meters_with_account_and_statuses | issue_check_execution_session | keep | | | view column | +| meters_with_account_and_statuses | issue_check_last_run_at | keep | | | view column | +| meters_with_account_and_statuses | pole_id | keep | | | view column | +| meters_with_account_and_statuses | meter_phase | keep | | | view column | +| meters_with_account_and_statuses | last_metering_hardware_install_session_id | keep | | | view column | +| meters_with_account_and_statuses | kwh_tariff | keep | | | view column | +| meters_with_account_and_statuses | watchdog_session | drop | §4b | Follows meters column drop | view column | +| meters_with_account_and_statuses | watchdog_last_run_at | drop | §4b | Follows meters column drop | view column | +| meters_with_account_and_statuses | version | keep | | | view column | +| meters_with_account_and_statuses | is_simulated | drop | §8 | Follows meters column drop | view column; G2 signed off | +| meters_with_account_and_statuses | power_limit_hps_mode | keep | | | view column | +| meters_with_account_and_statuses | current_special_status | drop | §1 | Follows meters column drop | view column | +| meters_with_account_and_statuses | communication_protocol | keep | | | view column | +| meters_with_account_and_statuses | is_cabin_meter | keep | | | view column | +| meters_with_account_and_statuses | last_encountered_issue_id | keep | | | view column | +| meters_with_account_and_statuses | connection_id | keep | | | view column | +| meters_with_account_and_statuses | dcu_id | keep | | | view column | +| meters_with_account_and_statuses | decoder_key | keep | | | view column | +| meters_with_account_and_statuses | last_sts_token_issued_at | keep | | | view column | +| meters_with_account_and_statuses | rls_grid_id | keep | | | view column | +| meters_with_account_and_statuses | rls_organization_id | keep | | | view column | +| meters_with_account_and_statuses | is_test_mode_on | keep | | | view column | +| meters_with_account_and_statuses | pulse_counter_kwh | drop | §8 | Follows meters column drop | view column; G2 signed off | +| meters_with_account_and_statuses | device_id | drop | §4 | Follows meters column drop | view column | +| meters_with_account_and_statuses | grid_id | keep | | | view column | +| meters_with_account_and_statuses | is_hidden_from_reporting | keep | | | view column | +| meters_with_account_and_statuses | full_name | keep | | | view column | +| meters_with_account_and_statuses | phone | keep | | | view column | +| meters_with_account_and_statuses | install_status | keep | | | view column | +| meters_with_account_and_statuses | open_issue | keep | | | view column | + +**Actions:** `keep` | `drop` | `rename` + +**Workflow:** + +1. ~~Maintainer reviews in batches (G1 ✓, G2 ✓, G3 ✓, G4 notifications + field ops ✓).~~ **Task 3b complete.** Enum/function/trigger review → Task 3c. +2. Edit actions here; add rationale for new drops/renames. +3. On sign-off per batch: sync confirmed `drop`/`rename` rows to deviation register **Column adjustments**. +4. Task 3 complete when every row has final action and register is synced. + +**Not in scope:** columns on excluded/dropped tables. Enum **value** trimming is a separate pass. diff --git a/docs/plans/002-oss-migration/002b-schema-deviation-register.md b/docs/plans/002-oss-migration/002b-schema-deviation-register.md index ceeb0d4..079f98b 100644 --- a/docs/plans/002-oss-migration/002b-schema-deviation-register.md +++ b/docs/plans/002-oss-migration/002b-schema-deviation-register.md @@ -8,9 +8,14 @@ spec for the convergence data migration that brings the company DB in line with **Rules:** - One row per deviation. No deviation ships in the init migration without a row here. +- **Column-level changes** on otherwise-**keep** tables (drop/rename/type change) are recorded in the + **Column adjustments** section below — grouped under the register entry that motivates them, not + as separate inventory rows. - `Cutover implication` is mandatory: what must happen to the company production DB (drop X, rename Y, archive/relink Z, nothing) for it to converge on the baseline. -- Entries are appended/amended during 002b Task 3 (classification) and Task 6 (verification), +- Entries are appended/amended during 002b Task 3a (object classification), **Task 3b** + (column review — see `002b-schema-column-adjustments.md`), **Task 3c** (programmability — + see `002b-schema-programmability-review.md`), and Task 6 (verification), and later by capability imports if they touch schema (they shouldn't — flag it if they do). - Status: `candidate` (from ADRs, unconfirmed) → `confirmed` (maintainer sign-off) or `rejected` (kept in baseline after all; row stays for the record). @@ -19,14 +24,521 @@ spec for the convergence data migration that brings the company DB in line with | # | Object(s) | Change | Rationale | Cutover implication | Status | |---|---|---|---|---|---| -| 1 | `directives`, `lorawan-directives` tables + their enums | Exclude (deprecated) | Superseded by `meter-interactions`; retained in company DB only for historical `orders` references (ADR-004 decision 9, ADR-008) | Archive/relink historical `orders` references, then drop tables + enums | candidate | -| 2 | `grafana_readonly` role + its grants | Parameterize (company infra) | Company-specific observability access, not part of a generic baseline (ADR-004 decision 3) | None (role may stay in company DB); provide as optional documented SQL | candidate | -| 3 | `make_readonly` role + its grants | Parameterize (company infra) | Company-specific Make.com integration access (ADR-004 decision 3) | None (role may stay in company DB); provide as optional documented SQL | candidate | -| 4 | Tables/enums behind `一`-prefixed modules (`一demo`, `一directive-watchdog-sessions`, `一meter-credit-transfers`) — exact objects identified in 002b Task 2/3 | Drop (dead) | Modules are dead code; their schema is dead weight (ADR-008 Phase 2) | Drop the corresponding objects from company DB (verify no data worth archiving first) | candidate | +| 1 | **Exclude — deprecated directive command system:** tables `directives`, `lorawan_directives`; view `batch_commands`; enums `directive_direction`, `directive_error`, `directive_phase`, `directive_status`, `directive_type`, `directive_special_status`; sequence `lorawan_directives_id_seq`; triggers on deprecated tables; indexes on deprecated tables | Exclude (deprecated) | Superseded by `meter-interactions`; historical `orders` references (ADR-004 decision 9, ADR-008). `directive_batches` / `directive_batch_executions` **renamed** to `meter_command_batches` / `meter_command_batch_executions` (register #16). Live batch path uses `task_type` + `fs_command`, not `directive_type` | Archive/relink historical `orders` refs; then drop excluded objects. See column adjustments §1 | confirmed | +| 2 | `grafana_readonly` role + grants + 15 `TO grafana_readonly` RLS policies | Parameterize (company infra) | Company-specific observability access, not part of a generic baseline (ADR-004 decision 3) | None (may stay in company DB); operator recipe `docs/database/optional/grafana-readonly.sql` — not in `supabase/migrations/` | confirmed | +| 3 | `make_readonly` role + grants + 4 `TO make_readonly` RLS policies | Parameterize (company infra) | Company-specific Make.com read access (ADR-004 decision 3) | None (may stay in company DB); operator recipe `docs/database/optional/make-readonly.sql` — not in `supabase/migrations/` | confirmed | +| 5 | `snaplet_readonly_2` role + grants on `public` + `auth` | Parameterize (company infra) | Snaplet seeding tooling; not generic OSS infra | None (may stay in company DB); operator recipe `docs/database/optional/snaplet-readonly.sql` — not in `supabase/migrations/` | confirmed | +| 6 | `notify_make_about_*` functions (3) + triggers on `grids` (3) | Parameterize (company infra) | Make.com grid webhooks; Tier-3 integration (ADR-004) | None (may stay in company DB); operator recipe `docs/database/optional/make-grid-triggers.sql` — not in `supabase/migrations/` | confirmed | +| 4 | **Drop — dead `一`-prefixed modules:** table `directive_watchdog_sessions`, sequence `directive_watchdog_sessions_id_seq` (`一directive-watchdog-sessions`; `一demo` has no migration objects) | Drop (dead) | Module disabled/WIP; watchdog sessions only referenced from excluded directive tables (ADR-008 Phase 2) | Drop from company DB; drop `meters.watchdog_session`, `meters.watchdog_last_run_at` (column adjustments §4b) | confirmed | +| 7 | **Exclude — deprecated meter credit transfers:** table `meter_credit_transfers`, enum `meter_credit_transfer_status_enum`, sequence `meter_credit_transfers_id_seq`, trigger `append_rls_organization_id_on_meter_credit_transfer_insert`, function `append_rls_organization_id_by_receiver_meter_id()` | Exclude (deprecated) | `一meter-credit-transfers` module disabled; superseded path absent from OSS baseline; historical `orders` references | Archive/relink historical `orders` refs; then drop excluded objects. See column adjustments §2 | confirmed | +| 8 | **Drop — abandoned onboarding feature flags:** tables `features`, `member_feature`; sequence `features_id_seq`; indexes on `member_feature`; 2 RLS policies on `features` | Drop (dead) | “Features seen” UX never shipped; pegasus integration commented out; no backend module | Drop from company DB; no archive required | confirmed | +| 9 | **Drop — legacy TypeORM migration ledger:** table `public.migrations`, sequence `migrations_id_seq` | Drop (dead) | Superseded by Supabase migrations (`legacy/supabase/migrations`); primary DB TypeORM has `synchronize: false` and no migration runner | Drop from company DB; no archive required | confirmed | +| 10 | **Rename — router insert trigger typo:** trigger `append_rls_organization_id_on_route_insert` → `append_rls_organization_id_on_router_insert` on `routers` | Rename | Legacy misname (`route` vs table `routers`); no semantic change | Company DB: `ALTER TRIGGER … RENAME` at cutover (or recreate in place); init migration uses corrected name | confirmed | +| 11 | **Extensions — init migration adds only non-default required:** omit Supabase platform defaults (`pg_stat_statements`, `pgcrypto`, `supabase_vault`, `uuid-ossp`); omit advisor tooling (`hypopg`, `index_advisor`); omit `pg_graphql` (off by default on new hosted projects); omit `pgjwt` (drop — see rationale). **Include** `IF NOT EXISTS`: `postgis`, `pg_net`, `pgsodium` | Omit from init / drop | Supabase fresh image already enables core extensions (verified `supabase/postgres` schema-17); avoid redundant `CREATE EXTENSION` and stale version anxiety; `postgis` required for `geometry` columns. **`pgjwt` dropped (amended 2026-07-10):** not available on Postgres 17 at all (Supabase removed it from the PG17 image bundle, alongside `timescaledb`/`plv8`/`plcoffee`/`plls`) — `CREATE EXTENSION IF NOT EXISTS "pgjwt"` would error outright on any PG17 project. Confirmed dead weight regardless: grepped the full legacy migration chain and all app code for its functions (`sign`/`verify`/`url_encode`/`url_decode`/`algorithm_sign`) — zero usage beyond the bare `CREATE EXTENSION` line. Matches Supabase's own guidance ("if you weren't explicitly using pgjwt... it's most likely safe to disable") | Company DB: no-op if extensions already present; vanilla Postgres adopters follow README extension prerequisites. `pgjwt`: drop from company DB at cutover (confirmed unused — no functional loss) | confirmed | +| 12 | **Drop — generic device registry (device-data-sink):** tables `devices`, `device_types`, `device_logs`; sequences `devices_id_seq`, `device_types_id_seq`, `device_logs_id_seq`; function `append_rls_organization_id_by_device_id()`; triggers on `devices` / `device_logs`; RLS policies; realtime publication entries | Drop (dead) | Device-data-sink path removed from OSS scope (ADR-004); no active app usage; Grafana read policies only | Drop from company DB; drop `meters.device_id` FK/unique (see column adjustments §4) | confirmed | +| 13 | **Drop — payouts module:** tables `payouts`, `bank_accounts`; enum `payout_status_enum`; sequences `payouts_id_seq`, `bank_accounts_id_seq`; function `lock_next_order()`; RLS policies on `payouts` / `bank_accounts` | Drop (dead) | Payouts module dropped from OSS scope; `bank_accounts` only used by `payouts`; `lock_next_order` superseded by `lock_next_order_and_wallets` | Drop from company DB; drop `grids.is_automatic_payout_generation_enabled` (column adjustments §3) | confirmed | +| 14 | **Drop — pd-hero workflow (WIP):** tables `pd_flows`, `pd_flow_templates`, `pd_sections`, `pd_section_templates`, `pd_actions`, `pd_action_templates`, `pd_documents`, `pd_document_templates`, `pd_audits`; enums `pd_action_status_enum`, `pd_action_type_enum`, `pd_document_type_enum`; sequences `pd_flows_id_seq`, `pd_flow_templates_id_seq`, `pd_sections_id_seq`, `pd_section_templates_id_seq`, `pd_actions_id_seq`, `pd_action_templates_id_seq`, `pd_documents_id_seq`, `pd_document_templates_id_seq`, `pd_audits_id_seq`; function `lock_next_pd_action()`; RLS policies on dropped tables | Drop (dead) | pd-hero Make.com/Google workflow under-developed; only `pd_sites` / `pd_site_submissions` kept for site pipeline. Likely replaced by different design | Drop from company DB; drop `pd_sites.pd_flow_id`, `organizations.pd_hero_google_drive_folder_id` (column adjustments §5) | confirmed | +| 15 | **Drop — autopilot execution log:** table `autopilot_executions`, sequence `autopilot_executions_id_seq`; RLS policies | Drop (deferred) | Autopilot deferred from OSS baseline; likely returns as separate microservice with its own persistence | Drop from company DB; drop `grids.telegram_response_path_autopilot` (column adjustments §15) | confirmed | +| 16 | **Rename — meter command batches:** tables `directive_batches` → `meter_command_batches`, `directive_batch_executions` → `meter_command_batch_executions`; sequences `directive_batches_id_seq` → `meter_command_batches_id_seq`, `directive_batch_executions_id_seq` → `meter_command_batch_executions_id_seq`; indexes `idx_directive_batches_rls_organization_id` → `idx_meter_command_batches_rls_organization_id`, `idx_directive_batch_executions_rls_organization_id` → `idx_meter_command_batch_executions_rls_organization_id`; function `append_rls_organization_id_by_directive_batch_id()` → `append_rls_organization_id_by_meter_command_batch_id()`; triggers `append_rls_organization_id_on_directive_batch_insert` → `append_rls_organization_id_on_meter_command_batch_insert`, `append_rls_organization_id_on_directive_batch_execution_insert` → `append_rls_organization_id_on_meter_command_batch_execution_insert`; FK `meter_interactions.batch_execution_id` → `meter_command_batch_executions`; column `directive_batch_id` → `meter_command_batch_id` on executions table | Rename | Legacy `directive_*` naming from deprecated directive system; live path is meter-interactions / command batches | Company DB: `ALTER TABLE … RENAME` (+ sequence/index/function/trigger renames); init migration uses new names. See column adjustments §6 | confirmed | +| 17 | **Column prunes — platform core (Task 3b G1):** `organizations.phone`, `organizations.address`, `api_keys.is_locked`, `dcus.queue_buffer_length`, `grids.are_all_dcus_online`, `grids.are_all_dcus_under_high_load_threshold`, `grids.meter_consumption_issue_threshold_detection_days`, `grids.meter_communication_issue_threshold_detection_days`, `grids.uses_dual_meter_setup` | Drop columns | Maintainer sign-off 2026-07-09 — unused or superseded in OSS baseline | Drop columns at cutover; platform-core import updates entities/DTOs | confirmed | +| 18 | **Column prunes — metering (Task 3b G2):** `directive_batches.lock_session`, `directive_batches.execution_bucket`, `meter_commissionings.initialised_steps` / `pending_steps` / `processing_steps` / `successful_steps` / `failed_steps` / `total_steps`, `meter_commissionings.lock_session`, `meters.power_down_count`, `meters.power_down_count_updated_at`, `meters.is_simulated`, `meters.pulse_counter_kwh`, `meters.pulse_counter_kwh_updated_at` (+ matching view columns on `meters_with_account_and_statuses`) | Drop columns | Maintainer sign-off 2026-07-09 — unused or superseded in OSS baseline | Drop columns at cutover; metering import updates entities/DTOs and view definition | confirmed | +| 19 | **Column prunes — payments + production monitoring (Task 3b G3):** `wallets.goldring_migration_id`, `orders.external_system` | Drop columns | Maintainer sign-off 2026-07-09 — migration-era / unused in OSS baseline | Drop columns at cutover; payments import updates order/wallet entities | confirmed | +| 20 | **Column changes — notifications + field ops (Task 3b G4):** `issues.external_system` → `external_tracking_system`, `issues.external_reference` → `external_tracking_reference`; drop `issues.estimated_lost_revenue`, `issues.snoozed_until`, `issues.mppt_id`, `issues.grid_id` | Rename + drop columns | Maintainer sign-off 2026-07-09 — clarify external tracking naming; prune unused issue columns | `ALTER TABLE … RENAME COLUMN` + drops at cutover; field-ops import updates issue entity/DTO | confirmed | +| 21 | **Drop — orphan RLS helper:** function `append_rls_organization_id_by_historical_grid_id()` | Drop (dead) | Task 3c H1a — no trigger in migration chain; `orders.historical_grid_id` set in app (`tiamat/order-meta.ts`); `transactions` use `append_rls_organization_id_by_order_id()` instead | Drop function from company DB at cutover (no-op if already unused); omit from init migration | confirmed | +| 22 | **Rename + redesign — admin organization becomes DB-native:** function `rls_check_if_nxt_member()` → `rls_check_if_admin_org_member()` (reads `PLATFORM_OPERATOR` org via indexed lookup, `LANGUAGE sql STABLE`); **add** enum value `organization_type_enum.PLATFORM_OPERATOR`; **add** partial unique index `one_platform_operator_org` on `organizations`. *(Amended 2026-07-13 Task 8: GUC + `sync_admin_organization_id_guc` trigger dropped — Supabase migrations run as non-superuser `postgres`; `ALTER DATABASE … SET` for custom GUCs is not viable.)* | Rename + redesign + add | Task 3c H1a/H1c — RLS cannot read the app config (`getConfig().deployment.adminOrganizationId`, ADR-007 decision 1/10); admin org must be DB-native. Full rationale: ADR-007 Amendment (2026-07-09), implementation amended 2026-07-13 | Company DB at cutover: add enum value + index; flag NXT Grid's organization row `organization_type = 'PLATFORM_OPERATOR'`; update RLS policies to `rls_check_if_admin_org_member()`; post-migration bootstrap per `docs/deployment/supabase.md` §5; app-side `getConfig().deployment.adminOrganizationId` consumers **not yet migrated** — open in ADR-007 | confirmed | +| 23 | **Redesign — `append_rls_*` trigger functions consolidated onto shared helpers:** 10 functions (`append_rls_organization_id_by_account_id/grid_id/connection_id/customer_id/customer_id_or_agent_id_or_connec/dcu_id_or_meter_id/directive_batch_id/meter_id/metering_hardware_install_session/order_id`) redesigned to delegate to **6 new** helper functions (`rls_org_id_from_grid/customer/connection/agent/meter/dcu`); dead join to `organizations` removed from `by_account_id()`; second dead lookup removed from the `organization_id` branch of `by_customer_id_or_agent_id_or_connec()`; `SET search_path TO ''` added to all 10 (9 were missing it) | Redesign + add | Task 3c H1b — same denormalize-via-trigger strategy kept (correct for read-heavy RLS), but join logic was duplicated near-verbatim across several functions (e.g. meter→connection→customer→grid appears 3×); consolidating removes duplication with no runtime cost (`LANGUAGE sql STABLE` helpers are inlining-eligible). `search_path` fix addresses Supabase "Function Search Path Mutable" linter warning | Company DB at cutover: `CREATE FUNCTION` the 6 helpers; `CREATE OR REPLACE FUNCTION` the 10 trigger functions with new bodies (same names/signatures/trigger wiring — no `ALTER TRIGGER` needed except the already-recorded #16 rename). See Programmability adjustments §4 | confirmed | +| 24 | **Harden — `lock_next_order_and_wallets()` search_path:** add `SET search_path TO ''` | Harden | Task 3c H1b spillover + H3 logic review — "Function Search Path Mutable" linter fix; function logic confirmed **keep** (atomic `FOR UPDATE SKIP LOCKED` payment lock, supersedes dropped `lock_next_order()`) | Company DB at cutover: `CREATE OR REPLACE FUNCTION` with `SET search_path TO ''` added; no behavior change | confirmed | +| 25 | **Add index — `idx_accounts_organization_id`** on `accounts(organization_id)` | Add index | Task 3c H1b — found while tracing RLS index coverage for register #23; `accounts` has 2 RLS policies (`Allow org member to select`, `Allow org members to update`) filtering directly on `organization_id`, but (unlike every other RLS-filtered org column in the schema) had no supporting index | Company DB: `CREATE INDEX IF NOT EXISTS` at cutover (no-op if already present); init migration includes it from the start | confirmed | +| 26 | **Redesign — `get_grid_status(grid_id)` return type:** drop `are_all_dcus_online` and `are_all_dcus_under_high_load_threshold` from `RETURNS TABLE`; mark function **`STABLE`** | Redesign | Task 3c H2 — columns dropped from `grids` in register #17; function still selected them (init migration would fail). Pegasus already fetches gateway status via separate `dcus` query; UI usage of RPC columns commented out. `STABLE` marking: read-only RPC, same fix-when-it-surfaces principle as H1c/H1b | Company DB at cutover: `CREATE OR REPLACE FUNCTION` with narrowed return type + `STABLE`; pegasus supabase types at production-monitoring import | confirmed | +| 27 | **Harden + fix — payments reporting RPCs:** `find_energy_topup_revenue()` marked **`STABLE`**; `find_top_spenders()` marked **`STABLE`** + `GROUP BY` narrowed to `meta_receiver_id_part_2`, `meta_receiver_name_part_2` only (drop `meta_receiver_id`/meter — fixes customer split across meters) | Harden + fix | Task 3c H3 — read-only RPCs should be `STABLE`; legacy `GROUP BY` included meter id so one customer with multiple meters appeared as separate partial rows in top-spender rankings (loch revenue reports) | Company DB at cutover: `CREATE OR REPLACE FUNCTION` both; no app code change required (return shape unchanged) | confirmed | +| 28 | **Enum value trim — `external_system_enum`:** drop values `JOTFORM`, `STEAMACO`, `ACREL` (keep 11 incl. `JIRA`) | Drop enum values | Task 3c H4a — unused/dead integrations; `JIRA` **kept** (required by `issues.external_tracking_system`, register #20 — same enum type, DB default `'JIRA'`) | Company DB at cutover: verify no rows reference dropped values on any `external_system_enum` column (dcus, meters, grids, issues, notifications, …); migrate/archive if found; init migration creates trimmed enum | confirmed | +| 29 | **Enum value trim — `notification_type_enum`:** drop value `AUTO_PAYOUT_GENRATION_REPORT` (keep 15) | Drop enum value | Task 3c H4d — payouts module dropped (register #13); only producer was dropped `loch/payouts.service` | Company DB at cutover: verify no `notifications.notification_type = 'AUTO_PAYOUT_GENRATION_REPORT'` rows (archive if found); init migration creates trimmed enum | confirmed | +| 30 | **Add indexes — D1 FK/RLS coverage sweep:** 47 new indexes on keep tables — 3 RLS-predicate/renamed-FK gaps (`idx_grids_organization_id`, `idx_metering_hardware_imports_rls_organization_id`, `idx_meter_command_batch_executions_meter_command_batch_id`) + 44 plain FK-column indexes across `agents`, `api_keys`, `audits` (8), `connection_requested_meters`, `dcus`, `energy_cabins`, `meter_command_batches` (2), `meter_commissionings`, `metering_hardware_imports` (1 more), `metering_hardware_install_sessions` (3), `meters.pole_id`, `mppts`, `notes` (4), `notifications` (4), `orders` (3), `pd_site_submissions`, `pd_sites` (2), `poles`, `routers`, `transactions`, `ussd_session_hops`, `ussd_sessions` (3). Full per-column list + rationale: `002b-schema-performance-audit.md` Indexes § Gap findings | Add index | Task 3d D1 — systematic FK/RLS-predicate index coverage sweep across all 35 keep tables. Tier 1 mirrors register #25 (RLS-predicate/FK column with no supporting index, same class of gap); Tier 2 is standard "index every FK" hygiene — largely absent except on `rls_organization_id`-style denormalized columns. Reviewed and excluded: 2 columns with no current read/RLS usage (`members.busy_commissioning_id`, `meters.rls_grid_id` — Performance adjustments §1 notes) and 2 orders partial/full index pairs confirmed intentional, not redundant | Company DB at cutover: `CREATE INDEX IF NOT EXISTS` for all 47 (no-op if already present); init migration includes them from the start | confirmed | +| 31 | **Mark `STABLE` — D2 remaining RLS-helper volatility:** `rls_check_if_lender()`, `rls_get_member_org_id()` | Harden (volatility) | Task 3d D2 — both are single-statement `auth.jwt()` reads with no side effects, same shape as `rls_check_if_admin_org_member()` (register #22, already `STABLE`). Every other keep function's volatility was already decided in Task 3c (H1b/H1c/H2/H3) — these 2 were the only ones left. `rls_get_member_org_id()` is the single most-invoked RLS helper in the schema (~24 policies across ~20 keep tables); `rls_check_if_lender()` used in 4. Full analysis: `002b-schema-performance-audit.md` Function volatility § D2 | Company DB at cutover: `CREATE OR REPLACE FUNCTION` both with `STABLE` added; no behavior change; both already have `SECURITY DEFINER` + `SET search_path TO ''` | confirmed | +| 32 | **Normalize RLS policy invocation pattern — D3:** wrap 18 bare helper-function calls in `( SELECT public.fn() AS fn )` — 14 × `rls_check_if_admin_org_member()` on `grids`/`meters`/`notes`/`organizations`/`pd_sites`/`poles`/`wallets` ("Allow NXT Grid to insert" `WITH CHECK`) and `accounts`/`connections`/`grids`/`meters`/`organizations`/`pd_site_submissions`/`pd_sites` ("Allow NXT Grid to update" `USING`); 4 × `rls_get_member_org_id()` on `notes`/`poles` ("Allow org members to insert" `WITH CHECK`) and `accounts`/`orders` ("Allow org members to update" `USING`) | Rewrite policy clause | Task 3d D3 — parsed all 123 `CREATE POLICY` statements (single migration, never altered later); found 69 helper-function calls across keep-table policies, 51 already wrapped (the Postgres/Supabase-recommended `InitPlan`-cacheable pattern) and 18 bare. Clean split: every bare call is on `INSERT`/`WITH CHECK` or `UPDATE`/`USING` — zero on `SELECT` (all 26 `SELECT`-side calls already wrapped). Normalizing removes the inconsistency and closes the bulk-`UPDATE` per-row-reevaluation gap; pairs with register #31 (`STABLE`), which is what makes the wrap's caching valid. Full list: `002b-schema-performance-audit.md` RLS policy invocation pattern § D3 | Company DB at cutover: `ALTER POLICY`/recreate the 18 policies with the wrapped clause; no behavior change (same boolean result), read-path performance only | confirmed | +| 33 | **Data API grants — keep explicit per-object grants; omit auto-expose default:** init migration carries forward `GRANT ALL ON TABLE/SEQUENCE/FUNCTION … TO "anon"/"authenticated"/"service_role"` for every **keep** table/sequence/function (mirrors the legacy dump, keep-bucket scope only); **omits** the 3 `ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES/SEQUENCES/FUNCTIONS TO "anon"/"authenticated"/"service_role"` statements | Keep (explicit) + omit (default-privileges) | Found during final Task 3d wrap-up sanity check: Supabase changed platform defaults — new projects created on/after 2026-05-30 no longer auto-grant `anon`/`authenticated`/`service_role` access to `public` tables (PostgREST returns `42501 permission denied` without an explicit `GRANT`, before RLS is even evaluated); enforced on all existing projects from 2026-10-30 (new tables added after that date, on any project). Explicit per-object grants on keep objects make the baseline self-contained — reachable via the Data API regardless of the project-creation toggle, local vs. hosted vs. self-hosted vanilla Postgres. Omitting the default-privileges statements is a deliberate choice to align with Supabase's now-recommended pattern (explicit `GRANT` + RLS + policy as one reviewable unit per migration) rather than re-introduce the auto-expose-everything default the platform itself is moving away from. Production's 57 existing tables are unaffected either way — grandfathered permanently per Supabase's changelog; this only governs objects added going forward | Company DB: no-op (existing grants/default-privileges setup untouched — this deviation only shapes the OSS baseline template, not company production). Adopters and 002c capability imports must add explicit `GRANT` statements for any new `public` table going forward — no ambient default; Task 9.2 verifies a keep table is actually reachable via the Data API on a fresh hosted project | confirmed | +| 34 | **FK cycle hardening — add `ON DELETE SET NULL` to 5 denormalized "latest pointer" FKs:** `meters.last_metering_hardware_install_session_id` → `metering_hardware_install_sessions.id`; `metering_hardware_install_sessions.last_metering_hardware_import_id` → `metering_hardware_imports.id`; `metering_hardware_install_sessions.last_meter_commissioning_id` → `meter_commissionings.id`; `dcus.last_metering_hardware_install_session_id` → `metering_hardware_install_sessions.id`; `meters.last_encountered_issue_id` → `issues.id` | Harden (`ON DELETE`) | Found during final sanity wrap-up (item 4/4): systematic FK-cycle sweep across all 35 keep tables found exactly **5 direct 2-cycles** (no cycles of length 3+) — each a denormalized "latest child" pointer column (`last_X_id`) paired with the child's own structural back-reference to its parent. Confirmed legitimate, not redundant: `dcus.service.ts` explicitly comments "we have a dcu property point at the latest dcu session, so it's easily retrievable"; the pattern is read throughout `meters_with_account_and_statuses` (chains 4 of the 5 cycles in one view), PostgREST embeds, and a TypeORM `@OneToOne`. Both sides of every cycle are already indexed (forward pointers via pre-existing `UNIQUE` constraints; structural back-pointers via register #30/Task 3d D1) — the query-efficiency half of the concern is already resolved. The real gap: all 10 FKs in these cycles default to `NO ACTION`, so deleting a row on either side while still referenced fails outright — matches the "hard to delete" pain reported. A full `legacy/` codebase sweep found **zero** hard-deletes (`.delete()`/`DELETE FROM`/`.remove()`) against any table — every removal is a soft-delete or status transition — so this isn't live-impacting today, but is a latent trap for future/manual/ops-level deletes. Fix scoped to the 5 *forward* pointers only; the 5 structural back-pointers correctly stay `NO ACTION` (a child row should not silently lose its real parent reference) | Company DB at cutover: drop + recreate each of the 5 FKs with `ON DELETE SET NULL` (no data impact — existing rows unaffected; changes only what happens on a future `DELETE`, which nothing currently triggers). See FK design adjustments §1 | confirmed | +| 35 | `grids.timezone` column default | Parameterize/neutralize default: `'Africa/Lagos'` → `'UTC'` | De-Nigeria the OSS baseline (002d decision 12; i18n register #1) | **New inserts only** — existing company grid rows keep stored values. At cutover: optional `ALTER TABLE grids ALTER COLUMN timezone SET DEFAULT 'UTC'` if NXT Grid wants the column default aligned; no row data migration required | confirmed | + +## Column adjustments + +Authoritative for init-migration column prunes on **keep** tables. **Working review copy:** +`002b-schema-column-adjustments.md` (Task 3b — one row per column; maintainer adds/removes drops +there, then confirmed rows are reflected here). + +Grouped under the register entry that motivates them. Applied in the init migration on **keep** +tables; company DB converges at cutover. + +### §1 — Motivated by register #1 (deprecated directive system) + +| Table / view | Column(s) | Change | Rationale | Cutover / code impact | +|---|---|---|---|---| +| `orders` | `directive_id`, `lorawan_directive_id` (+ FKs, unique on `directive_id`) | Drop | Only referenced excluded directive tables | Archive/relink historical orders first | +| `meter_command_batches` (was `directive_batches`) | `directive_type` | Drop | Legacy; live code uses `task_type` + `fs_command` (DTO has no `directive_type`) | Drop column; TypeORM entity updated at metering import | +| `meters` | `current_special_status` | Drop | Shadow of deprecated directive status; `issues` table is canonical | Drop column; `issues.service` today reads this column — fix at metering import to use `issues` | +| `meters_with_account_and_statuses` | `current_special_status` (view column) | Drop from view definition | Follows `meters` column drop | Recreate view without column | + +### §2 — Motivated by register #7 (deprecated meter credit transfers) + +| Table / view | Column(s) | Change | Rationale | Cutover / code impact | +|---|---|---|---|---| +| `orders` | `meter_credit_transfer_id` (+ FK, unique `REL_a53c58bdb5ae0193f17497c81b`) | Drop | Only referenced excluded `meter_credit_transfers` | Archive/relink historical orders first; remove tiamat `produceMeterInteraction` branch, reporting meta check, TypeORM join/relation at payments import | + +### §3 — Motivated by register #13 (drop payouts module) + +| Table / view | Column(s) | Change | Rationale | Cutover / code impact | +|---|---|---|---|---| +| `grids` | `is_automatic_payout_generation_enabled` | Drop | Payouts module dropped; toggle has no live path | Drop column at cutover; remove from grid entity/DTO at platform-core import | + +### §4 — Motivated by register #12 (drop device registry) + +| Table / view | Column(s) | Change | Rationale | Cutover / code impact | +|---|---|---|---|---| +| `meters` | `device_id` (+ FK, unique `meters_device_id_key`) | Drop | Only referenced dropped `devices` table | Drop column at cutover; metering import | +| `meters_with_account_and_statuses` | `device_id` (view column) | Drop from view definition | Follows `meters` column drop | Recreate view without column | + +### §4b — Motivated by register #4 (watchdog sessions dropped) + +| Table / view | Column(s) | Change | Rationale | Cutover / code impact | +|---|---|---|---|---| +| `meters` | `watchdog_session`, `watchdog_last_run_at` | Drop | Watchdog module dropped; columns commented out in entity | Drop columns at cutover; metering import | +| `meters_with_account_and_statuses` | `watchdog_session`, `watchdog_last_run_at` (view columns) | Drop from view definition | Follows `meters` column drop | Recreate view without columns | + +### §5 — Motivated by register #14 (drop pd-hero workflow) + +| Table / view | Column(s) | Change | Rationale | Cutover / code impact | +|---|---|---|---|---| +| `pd_sites` | `pd_flow_id` (+ FK to `pd_flows`) | Drop | Only referenced dropped pd-hero workflow tables; sites kept for pipeline/geo | Drop column at cutover; pegasus `PdSiteView` pd-flow actions UI removed or reworked at field-ops import | +| `organizations` | `pd_hero_google_drive_folder_id` | Drop | Only used by dropped pd-hero service (`loch/pd-hero`) | Drop column at cutover; remove from organization entity at platform-core import | + +### §6 — Motivated by register #16 (meter command batch renames) + +| Table / view | Column(s) | Change | Rationale | Cutover / code impact | +|---|---|---|---|---| +| `meter_command_batch_executions` (was `directive_batch_executions`) | `directive_batch_id` (+ FK to `meter_command_batches`) | Rename → `meter_command_batch_id` | Align column name with renamed parent table | `ALTER TABLE … RENAME COLUMN` at cutover; TypeORM entity updated at metering import | + +### §7 — Motivated by register #17 (platform core column prunes, Task 3b G1) + +| Table / view | Column(s) | Change | Rationale | Cutover / code impact | +|---|---|---|---|---| +| `organizations` | `phone`, `address` | Drop | Unused in OSS baseline | Drop columns at cutover; organization entity/DTO at platform-core import | +| `api_keys` | `is_locked` | Drop | Unused in OSS baseline | Drop column at cutover; api_keys entity at platform-core import | +| `dcus` | `queue_buffer_length` | Drop | Unused in OSS baseline | Drop column at cutover; dcu entity at platform-core import | +| `grids` | `are_all_dcus_online`, `are_all_dcus_under_high_load_threshold`, `meter_consumption_issue_threshold_detection_days`, `meter_communication_issue_threshold_detection_days`, `uses_dual_meter_setup` | Drop | Unused or superseded in OSS baseline | Drop columns at cutover; grid entity/DTO at platform-core import | + +### §15 — Motivated by register #15 (autopilot deferred) + +| Table / view | Column(s) | Change | Rationale | Cutover / code impact | +|---|---|---|---|---| +| `grids` | `telegram_response_path_autopilot` | Drop | Autopilot deferred; column only in types/entity | Drop column at cutover; grid entity at platform-core import | + +### §8 — Motivated by register #18 (metering column prunes, Task 3b G2) + +| Table / view | Column(s) | Change | Rationale | Cutover / code impact | +|---|---|---|---|---| +| `meter_command_batches` (was `directive_batches`) | `lock_session`, `execution_bucket` | Drop | Unused in OSS baseline | Drop columns at cutover; metering import | +| `meter_commissionings` | `initialised_steps`, `pending_steps`, `processing_steps`, `successful_steps`, `failed_steps`, `total_steps`, `lock_session` | Drop | Step counters and lock unused in OSS baseline | Drop columns at cutover; metering import | +| `meters` | `power_down_count`, `power_down_count_updated_at`, `is_simulated`, `pulse_counter_kwh`, `pulse_counter_kwh_updated_at` | Drop | Unused in OSS baseline | Drop columns at cutover; metering import | +| `meters_with_account_and_statuses` | same five columns (view columns) | Drop from view definition | Follows `meters` column drops | Recreate view without columns | + +### §9 — Motivated by register #19 (payments + production monitoring column prunes, Task 3b G3) + +| Table / view | Column(s) | Change | Rationale | Cutover / code impact | +|---|---|---|---|---| +| `wallets` | `goldring_migration_id` | Drop | Goldring migration-era ID; only in entity/types | Drop column at cutover; wallet entity at payments import | +| `orders` | `external_system` | Drop | Unused in OSS baseline | Drop column at cutover; order entity at payments import | + +### §10 — Motivated by register #20 (notifications + field ops, Task 3b G4) + +| Table / view | Column(s) | Change | Rationale | Cutover / code impact | +|---|---|---|---|---| +| `issues` | `external_system` | Rename → `external_tracking_system` | Distinguish external ticket tracker from integration `external_system` elsewhere | `ALTER TABLE … RENAME COLUMN` at cutover; issue entity at field-ops import | +| `issues` | `external_reference` | Rename → `external_tracking_reference` | Pair with tracking-system rename | `ALTER TABLE … RENAME COLUMN` at cutover; issue entity at field-ops import | +| `issues` | `estimated_lost_revenue`, `snoozed_until`, `mppt_id`, `grid_id` | Drop | Unused or superseded in OSS baseline (`grid_id` derivable via `meter_id`) | Drop columns at cutover; field-ops import | + +### §pending — Column prune backlog + +> _(empty — all provisional drops from Task 3a/3b resolved in G1–G4.)_ + +## Programmability adjustments + +Authoritative for init-migration changes to **keep** enums (value trims), functions, and triggers. +**Working review copy:** `002b-schema-programmability-review.md` (Task 3c). + +### §1 — Motivated by register #16 (meter command batch renames, Task 3c H1a) + +| Object | Change | Rationale | Cutover / code impact | +|--------|--------|-----------|------------------------| +| `append_rls_organization_id_by_directive_batch_id()` | Rename → `append_rls_organization_id_by_meter_command_batch_id()` | Register #16 — align with renamed `meter_command_batches` table | `ALTER FUNCTION … RENAME` at cutover; init uses new name | +| Trigger `append_rls_organization_id_on_directive_batch_insert` | Rename → `append_rls_organization_id_on_meter_command_batch_insert` ON `meter_command_batches` | Register #16 | `ALTER TRIGGER … RENAME` + table rename at cutover | +| Trigger `append_rls_organization_id_on_directive_batch_execution_insert` | Rename → `append_rls_organization_id_on_meter_command_batch_execution_insert` ON `meter_command_batch_executions` | Register #16 | `ALTER TRIGGER … RENAME` + table rename at cutover | +| Index `idx_directive_batches_rls_organization_id` | Rename → `idx_meter_command_batches_rls_organization_id` ON `meter_command_batches` | Register #16 | `ALTER INDEX … RENAME` at cutover | +| Index `idx_directive_batch_executions_rls_organization_id` | Rename → `idx_meter_command_batch_executions_rls_organization_id` ON `meter_command_batch_executions` | Register #16 | `ALTER INDEX … RENAME` at cutover | + +### §2 — Motivated by register #21 (orphan RLS helper drop, Task 3c H1a) + +| Object | Change | Rationale | Cutover / code impact | +|--------|--------|-----------|------------------------| +| `append_rls_organization_id_by_historical_grid_id()` | Drop function | Orphan — no trigger; never wired in chain | `DROP FUNCTION` at cutover if present; omit from init migration | + +### §3 — Motivated by register #22 (admin organization becomes DB-native, Task 3c H1c) + +| Object | Change | Rationale | Cutover / code impact | +|--------|--------|-----------|------------------------| +| `rls_check_if_nxt_member()` | Rename → `rls_check_if_admin_org_member()`; body redesigned to `SELECT id FROM organizations WHERE organization_type = 'PLATFORM_OPERATOR' LIMIT 1` (via `one_platform_operator_org` index); `LANGUAGE sql STABLE` | RLS cannot call `getConfig()`; DB-native admin org. **Amended 2026-07-13 (Task 8):** GUC + sync trigger dropped — Supabase `postgres` role cannot `ALTER DATABASE … SET` custom params | `DROP FUNCTION` old name + `CREATE FUNCTION` new name at cutover; update ~40 `"Allow NXT Grid"` policies to reference new name | +| `organization_type_enum` | Add value `PLATFORM_OPERATOR` | Marks the platform-operator organization row; not in legacy chain | `ALTER TYPE … ADD VALUE` at cutover; flag NXT Grid's org row | +| `organizations` | Add partial unique index `one_platform_operator_org` on `organization_type` WHERE `= 'PLATFORM_OPERATOR'` | Enforce at most one platform-operator org | `CREATE UNIQUE INDEX` at cutover | +| ~~`sync_admin_organization_id_guc()` + trigger~~ | **Dropped** (Task 8 amendment) | GUC mechanism required superuser `ALTER DATABASE`; not available to migration role or SQL-editor callers on Supabase | Omit from company cutover; not in init migration | + +### §4 — Motivated by register #23 (`append_rls_*` consolidation, Task 3c H1b) + +| Object | Change | Rationale | Cutover / code impact | +|--------|--------|-----------|------------------------| +| `rls_org_id_from_grid(grid_id)` | Add (new) | Leaf helper — `SELECT organization_id FROM grids WHERE id = grid_id`; `LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO ''` | `CREATE FUNCTION` at cutover | +| `rls_org_id_from_customer(customer_id)` | Add (new) | Delegates to `rls_org_id_from_grid()` via `customers.grid_id` | `CREATE FUNCTION` at cutover | +| `rls_org_id_from_connection(connection_id)` | Add (new) | Delegates to `rls_org_id_from_customer()` via `connections.customer_id` | `CREATE FUNCTION` at cutover | +| `rls_org_id_from_agent(agent_id)` | Add (new) | Delegates to `rls_org_id_from_grid()` via `agents.grid_id` | `CREATE FUNCTION` at cutover | +| `rls_org_id_from_meter(meter_id)` | Add (new) | Delegates to `rls_org_id_from_connection()` via `meters.connection_id` | `CREATE FUNCTION` at cutover | +| `rls_org_id_from_dcu(dcu_id)` | Add (new) | Delegates to `rls_org_id_from_grid()` via `dcus.grid_id` | `CREATE FUNCTION` at cutover | +| `append_rls_organization_id_by_account_id()` | Body redesign | Dead join to `organizations` removed; direct `SELECT organization_id FROM accounts WHERE id = NEW.account_id` | `CREATE OR REPLACE FUNCTION` at cutover; same name/trigger | +| `append_rls_organization_id_by_grid_id()` | Body redesign | `NEW.rls_organization_id := rls_org_id_from_grid(NEW.grid_id)` | `CREATE OR REPLACE FUNCTION` at cutover; same name/trigger | +| `append_rls_organization_id_by_connection_id()` | Body redesign | `NEW.rls_organization_id := rls_org_id_from_connection(NEW.connection_id)` | `CREATE OR REPLACE FUNCTION` at cutover; same name/trigger | +| `append_rls_organization_id_by_customer_id()` | Body redesign | `NEW.rls_organization_id := rls_org_id_from_customer(NEW.customer_id)` | `CREATE OR REPLACE FUNCTION` at cutover; same name/trigger | +| `append_rls_organization_id_by_customer_id_or_agent_id_or_connec()` | Body redesign | Each `IF/ELSIF` branch delegates to the matching helper; `organization_id` branch assigns `NEW.organization_id` directly (2nd dead lookup removed) | `CREATE OR REPLACE FUNCTION` at cutover; same name/trigger | +| `append_rls_organization_id_by_dcu_id_or_meter_id()` | Body redesign | Branches delegate to `rls_org_id_from_dcu()` / `rls_org_id_from_meter()` | `CREATE OR REPLACE FUNCTION` at cutover; same name/trigger | +| `append_rls_organization_id_by_directive_batch_id()` (→ `by_meter_command_batch_id()`, register #16) | Body redesign | Looks up `grid_id` from the batch row, delegates to `rls_org_id_from_grid()` | `CREATE OR REPLACE FUNCTION` at cutover under the renamed name | +| `append_rls_organization_id_by_meter_id()` | Body redesign | `NEW.rls_organization_id := rls_org_id_from_meter(NEW.meter_id)` | `CREATE OR REPLACE FUNCTION` at cutover; same name/trigger | +| `append_rls_organization_id_by_metering_hardware_install_session()` | Body redesign | Looks up `meter_id` from the session row, delegates to `rls_org_id_from_meter()` | `CREATE OR REPLACE FUNCTION` at cutover; same name/trigger | +| `append_rls_organization_id_by_order_id()` | Body redesign | Looks up `historical_grid_id` from the order row, delegates to `rls_org_id_from_grid()` | `CREATE OR REPLACE FUNCTION` at cutover; same name/trigger | +| All 10 functions above | Add `SET search_path TO ''` | Addresses Supabase "Function Search Path Mutable" linter warning (9 of 10 were missing it) | Included in the `CREATE OR REPLACE FUNCTION` bodies above | + +### §5 — Motivated by register #24 (`lock_next_order_and_wallets` search_path, Task 3c H1b spillover) + +| Object | Change | Rationale | Cutover / code impact | +|--------|--------|-----------|------------------------| +| `lock_next_order_and_wallets(uuid)` | Add `SET search_path TO ''` | Same linter fix as register #23; logic unchanged | `CREATE OR REPLACE FUNCTION` at cutover; no behavior change | + +### §6 — Motivated by register #25 (`accounts.organization_id` index, Task 3c H1b) + +| Object | Change | Rationale | Cutover / code impact | +|--------|--------|-----------|------------------------| +| `accounts` | Add index `idx_accounts_organization_id` on `organization_id` | Supports existing `Allow org member to select` / `Allow org members to update` RLS policies, which filter directly on this column with no prior index | `CREATE INDEX IF NOT EXISTS` at cutover | + +### §7 — Motivated by register #26 (`get_grid_status` redesign, Task 3c H2) + +| Object | Change | Rationale | Cutover / code impact | +|--------|--------|-----------|------------------------| +| `get_grid_status(grid_id integer)` | Drop `are_all_dcus_online` and `are_all_dcus_under_high_load_threshold` from `RETURNS TABLE`; remove from `SELECT` body; mark **`STABLE`** | Register #17 drops those `grids` columns; function must align or init migration fails. Pegasus gateway alerts use `dcus.is_online` query instead | `CREATE OR REPLACE FUNCTION` at cutover; pegasus types at production-monitoring import | + +### §8 — Motivated by register #27 (payments reporting RPCs, Task 3c H3) + +| Object | Change | Rationale | Cutover / code impact | +|--------|--------|-----------|------------------------| +| `find_energy_topup_revenue(grid_id, start_date, end_date)` | Mark **`STABLE`** | Read-only aggregate; no side effects | `CREATE OR REPLACE FUNCTION` at cutover | +| `find_top_spenders(...)` | Mark **`STABLE`**; `GROUP BY meta_receiver_id_part_2, meta_receiver_name_part_2` only (drop `meta_receiver_id`) | Customer-level top-spender aggregation; legacy meter id in GROUP BY split one customer across rows | `CREATE OR REPLACE FUNCTION` at cutover; return shape unchanged | + +### §9 — Motivated by register #28 (`external_system_enum` value trim, Task 3c H4a) + +| Object | Change | Rationale | Cutover / code impact | +|--------|--------|-----------|------------------------| +| `external_system_enum` | Omit values `JOTFORM`, `STEAMACO`, `ACREL` from init migration | JOTFORM: pd-hero dropped (#14); STEAMACO/ACREL: no live code refs. **JIRA retained** — `issues.external_tracking_system` uses this enum with default `'JIRA'` | Company DB: audit all columns typed `external_system_enum`; migrate rows off dropped values before enum recreation at cutover | + +### §10 — Motivated by register #29 (`notification_type_enum` value trim, Task 3c H4d) + +| Object | Change | Rationale | Cutover / code impact | +|--------|--------|-----------|------------------------| +| `notification_type_enum` | Omit value `AUTO_PAYOUT_GENRATION_REPORT` from init migration | Payouts module dropped (register #13); sendgrid case for this type becomes dead code at notifications import | Company DB: audit/archive `notifications` rows with this type before enum recreation at cutover | + +### §pending — Programmability backlog + +> None — Task 3c complete (2026-07-09). + +## Performance adjustments + +Authoritative for init-migration index additions/renames/removals and other structural performance +changes on **keep** objects. **Working review copy:** `002b-schema-performance-audit.md` (Task 3d). + +### §1 — Motivated by register #30 (D1 index coverage sweep, Task 3d) + +**Tier 1 — RLS-predicate / renamed-FK gaps:** + +| Table | Index (new) | Column | Rationale | +|---|---|---|---| +| `grids` | `idx_grids_organization_id` | `organization_id` | RLS predicate ("Allow org members to select") + FK; same class of gap as register #25 | +| `metering_hardware_imports` | `idx_metering_hardware_imports_rls_organization_id` | `rls_organization_id` | RLS predicate; table had zero non-PK indexes | +| `meter_command_batch_executions` | `idx_meter_command_batch_executions_meter_command_batch_id` | `meter_command_batch_id` (renamed from `directive_batch_id`, register #16) | Parent-lookup FK, unindexed | + +**Tier 2 — plain FK-column hygiene (44 indexes):** + +| Table | Column | Index (new) | +|---|---|---| +| `agents` | `grid_id` | `idx_agents_grid_id` | +| `api_keys` | `account_id` | `idx_api_keys_account_id` | +| `audits` | `agent_id` | `idx_audits_agent_id` | +| `audits` | `author_id` | `idx_audits_author_id` | +| `audits` | `connection_id` | `idx_audits_connection_id` | +| `audits` | `customer_id` | `idx_audits_customer_id` | +| `audits` | `dcu_id` | `idx_audits_dcu_id` | +| `audits` | `grid_id` | `idx_audits_grid_id` | +| `audits` | `member_id` | `idx_audits_member_id` | +| `audits` | `meter_id` | `idx_audits_meter_id` | +| `audits` | `organization_id` | `idx_audits_organization_id` | +| `connection_requested_meters` | `connection_id` | `idx_connection_requested_meters_connection_id` | +| `dcus` | `grid_id` | `idx_dcus_grid_id` | +| `energy_cabins` | `grid_id` | `idx_energy_cabins_grid_id` | +| `meter_command_batches` | `author_id` | `idx_meter_command_batches_author_id` | +| `meter_command_batches` | `grid_id` | `idx_meter_command_batches_grid_id` | +| `meter_commissionings` | `metering_hardware_install_session_id` | `idx_meter_commissionings_metering_hardware_install_session_id` | +| `metering_hardware_imports` | `metering_hardware_install_session_id` | `idx_metering_hardware_imports_metering_hardware_install_session_id` | +| `metering_hardware_install_sessions` | `author_id` | `idx_metering_hardware_install_sessions_author_id` | +| `metering_hardware_install_sessions` | `dcu_id` | `idx_metering_hardware_install_sessions_dcu_id` | +| `metering_hardware_install_sessions` | `meter_id` | `idx_metering_hardware_install_sessions_meter_id` | +| `meters` | `pole_id` | `idx_meters_pole_id` | +| `mppts` | `grid_id` | `idx_mppts_grid_id` | +| `notes` | `author_id` | `idx_notes_author_id` | +| `notes` | `connection_id` | `idx_notes_connection_id` | +| `notes` | `customer_id` | `idx_notes_customer_id` | +| `notes` | `meter_id` | `idx_notes_meter_id` | +| `notifications` | `account_id` | `idx_notifications_account_id` | +| `notifications` | `grid_id` | `idx_notifications_grid_id` | +| `notifications` | `notification_parameter_id` | `idx_notifications_notification_parameter_id` | +| `notifications` | `organization_id` | `idx_notifications_organization_id` | +| `orders` | `author_id` | `idx_orders_author_id` | +| `orders` | `receiver_wallet_id` | `idx_orders_receiver_wallet_id` | +| `orders` | `sender_wallet_id` | `idx_orders_sender_wallet_id` | +| `pd_site_submissions` | `organization_id` | `idx_pd_site_submissions_organization_id` | +| `pd_sites` | `operations_grid_id` | `idx_pd_sites_operations_grid_id` | +| `pd_sites` | `organization_id` | `idx_pd_sites_organization_id` | +| `poles` | `grid_id` | `idx_poles_grid_id` | +| `routers` | `grid_id` | `idx_routers_grid_id` | +| `transactions` | `order_id` | `idx_transactions_order_id` | +| `ussd_session_hops` | `ussd_session_id` | `idx_ussd_session_hops_ussd_session_id` | +| `ussd_sessions` | `account_id` | `idx_ussd_sessions_account_id` | +| `ussd_sessions` | `bank_id` | `idx_ussd_sessions_bank_id` | +| `ussd_sessions` | `meter_id` | `idx_ussd_sessions_meter_id` | + +**Reviewed and excluded (no index added):** + +| Table | Column | Reason | +|---|---|---| +| `members` | `busy_commissioning_id` | FK to `grids`, write-only (invite/update member) — no query filters by it; traced all usages in `legacy/` | +| `meters` | `rls_grid_id` | FK to `grids`, denormalized alongside `rls_organization_id` on meter assignment — but no RLS policy or query reads it; write-only today | + +**Reviewed, no change (not redundant):** `orders` full+partial index pairs on `historical_grid_id` and +`meta_receiver_id` — partial indexes serve the `ENERGY_TOPUP` hot path; both kept. + +**Cutover / code impact:** `CREATE INDEX IF NOT EXISTS` for all 47 at cutover (no-op if already +present); init migration includes them from the start; no app code changes (pure read-path +performance, no behavior change). + +### §2 — Motivated by register #31 (D2 function volatility, Task 3d) + +| Object | Change | Rationale | Cutover / code impact | +|--------|--------|-----------|------------------------| +| `rls_check_if_lender()` | Mark `STABLE` | Single-statement `auth.jwt()` read, no side effects; same shape as register #22 | `CREATE OR REPLACE FUNCTION` at cutover; no behavior change | +| `rls_get_member_org_id()` | Mark `STABLE` | Single-statement `auth.jwt()` read, no side effects; most-invoked RLS helper in the schema (~24 policies) | `CREATE OR REPLACE FUNCTION` at cutover; no behavior change | + +All other keep functions' volatility was already decided in Task 3c (registers #22, #23, #26, #27) or +is correctly left at the implicit `VOLATILE` default (functions with real side effects — `INSERT`, +`UPDATE`, `set_config()` — or trigger functions, where volatility marking has no planner effect since +they're invoked once per row by the trigger manager rather than through query-expression evaluation). +No further volatility work remains for any keep function. + +### §3 — Motivated by register #32 (D3 RLS policy invocation pattern, Task 3d) + +| Table | Policy | Clause | Function | Change | +|---|---|---|---|---| +| `grids` | Allow NXT Grid to insert | `WITH CHECK` | `rls_check_if_admin_org_member()` | Wrap in `( SELECT … AS … )` | +| `meters` | Allow NXT Grid to insert | `WITH CHECK` | `rls_check_if_admin_org_member()` | Wrap | +| `notes` | Allow NXT Grid to insert | `WITH CHECK` | `rls_check_if_admin_org_member()` | Wrap | +| `organizations` | Allow NXT Grid to insert | `WITH CHECK` | `rls_check_if_admin_org_member()` | Wrap | +| `pd_sites` | Allow NXT Grid to insert | `WITH CHECK` | `rls_check_if_admin_org_member()` | Wrap | +| `poles` | Allow NXT Grid to insert | `WITH CHECK` | `rls_check_if_admin_org_member()` | Wrap | +| `wallets` | Allow NXT Grid to insert | `WITH CHECK` | `rls_check_if_admin_org_member()` | Wrap | +| `accounts` | Allow NXT Grid to update | `USING` | `rls_check_if_admin_org_member()` | Wrap | +| `connections` | Allow NXT Grid to update | `USING` | `rls_check_if_admin_org_member()` | Wrap | +| `grids` | Allow NXT Grid to update | `USING` | `rls_check_if_admin_org_member()` | Wrap | +| `meters` | Allow NXT Grid to update | `USING` | `rls_check_if_admin_org_member()` | Wrap | +| `organizations` | Allow NXT Grid to update | `USING` | `rls_check_if_admin_org_member()` | Wrap | +| `pd_site_submissions` | Allow NXT Grid to update | `USING` | `rls_check_if_admin_org_member()` | Wrap | +| `pd_sites` | Allow NXT Grid to update | `USING` | `rls_check_if_admin_org_member()` | Wrap | +| `notes` | Allow org members to insert | `WITH CHECK` | `rls_get_member_org_id()` | Wrap | +| `poles` | Allow org members to insert | `WITH CHECK` | `rls_get_member_org_id()` | Wrap | +| `accounts` | Allow org members to update | `USING` | `rls_get_member_org_id()` | Wrap | +| `orders` | Allow org members to update | `USING` | `rls_get_member_org_id()` | Wrap | + +**Unchanged (51 policies):** every `SELECT` policy calling one of the 3 RLS helper functions, across +all 35 keep tables, is already wrapped — no action. + +**Cutover / code impact:** policy bodies only; no table/column/app changes. Company DB at cutover: +recreate the 18 listed policies with the wrapped clause (same boolean semantics, no behavior change). + +### §pending — Performance backlog + +> None — Task 3d complete (2026-07-10). D1–D4 all signed off; see +> `002b-schema-performance-audit.md`. + +## Data API access adjustments + +Authoritative for init-migration decisions about Postgres `GRANT`/`ALTER DEFAULT PRIVILEGES` +statements that govern Data API (PostgREST/GraphQL) reachability on **keep** objects. Not part of +Task 3d (performance) — found during the final pre-close sanity check on 2026-07-10, prompted by a +Supabase platform default change discovered mid-review. + +### §1 — Motivated by register #33 (Data API grants) + +| Object(s) | Change | Rationale | Cutover / code impact | +|-----------|--------|-----------|------------------------| +| Every keep table, sequence, function | Keep `GRANT ALL ON TABLE/SEQUENCE/FUNCTION … TO "anon"/"authenticated"/"service_role"` (mirrors legacy dump, keep-bucket scope) | Required for Data API reachability on any project created on/after 2026-05-30 (Supabase no longer auto-grants); makes the baseline self-contained regardless of project-creation toggle or hosting mode | Init migration includes these grants from the start; Task 5 must **not** treat them as "supabase-managed grant noise" to strip | +| 3 `ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES/SEQUENCES/FUNCTIONS TO "anon"/"authenticated"/"service_role"` statements | Omit from init migration | Deliberate: aligns with Supabase's now-recommended pattern (explicit grant + RLS + policy per migration, no ambient auto-expose of future tables) rather than re-introducing the default the platform itself is retiring | Company DB: no-op, existing default-privileges setup untouched (production tables grandfathered). 002c capability imports and any adopter migrations must add explicit `GRANT` statements for new `public` tables going forward | + +**Cutover / code impact:** no company DB change (existing grants/default-privileges untouched either +way — production tables are grandfathered regardless per Supabase's changelog). Governs only the OSS +baseline template and future migrations (002c onward). Task 9.2 must verify a keep table is actually +reachable via the Data API on a fresh hosted project (not just "no errors in dashboard") — this is a +new done-when criterion, not covered by the original Task 9 wording. + +## FK design adjustments + +Authoritative for init-migration changes to FK `ON DELETE`/`ON UPDATE` behavior on **keep** objects. +Not part of Task 3d (which covered indexes, function volatility, RLS invocation pattern, and +trigger/view design, but not FK relationship shape) — found during the final pre-close sanity check +on 2026-07-10, prompted by the maintainer's recollection of "back-and-forth" FK pairs. + +### §1 — Motivated by register #34 (FK cycle hardening) + +**Method:** enumerated every FK constraint in the legacy chain, filtered to edges where both +endpoints are keep tables, and searched the resulting graph for cycles. Result: **5 direct 2-cycles, +0 cycles of length 3+** — the full set, confirmed by an independent sweep of every `last_`/`latest_` +prefixed column name across all 35 keep tables (only 6 such columns exist; the 6th, +`pd_actions.latest_pd_document_id`, is on a dropped table and out of scope). + +| Forward pointer FK (change) | Structural back-pointer (unchanged) | +|---|---| +| `meters.last_metering_hardware_install_session_id` → `metering_hardware_install_sessions.id` | `metering_hardware_install_sessions.meter_id` → `meters.id` | +| `metering_hardware_install_sessions.last_metering_hardware_import_id` → `metering_hardware_imports.id` | `metering_hardware_imports.metering_hardware_install_session_id` → `metering_hardware_install_sessions.id` | +| `metering_hardware_install_sessions.last_meter_commissioning_id` → `meter_commissionings.id` | `meter_commissionings.metering_hardware_install_session_id` → `metering_hardware_install_sessions.id` | +| `dcus.last_metering_hardware_install_session_id` → `metering_hardware_install_sessions.id` | `metering_hardware_install_sessions.dcu_id` → `dcus.id` | +| `meters.last_encountered_issue_id` → `issues.id` | `issues.meter_id` → `meters.id` | + +**Change:** add `ON DELETE SET NULL` to the 5 forward pointer FKs (left column) only. The 5 +structural back-pointers (right column) are unchanged — they stay `NO ACTION`, since a +session/import/commissioning/issue should never silently lose its real parent reference. + +**Rationale:** all 5 forward pointers are a deliberate, actively-used "denormalized latest child" +cache — not redundant. Confirmed via `dcus.service.ts` ("we have a dcu property point at the latest +dcu session, so it's easily retrievable"), the `meters_with_account_and_statuses` view (chains 4 of +the 5 cycles in one query), PostgREST embeds (`meter-installs.service.ts`, +`meter-uninstalls.service.ts`), and a TypeORM `@OneToOne` (`meter.entity.ts`). Both sides of every +cycle are already indexed — forward pointers via pre-existing `UNIQUE` constraints, structural +back-pointers via register #30 (Task 3d D1) — so query efficiency is not a gap. The one real gap: +every one of these 10 FKs defaults to `NO ACTION`, so deleting a row on either side while still +referenced fails outright. A full sweep of `legacy/` found **zero** hard-deletes +(`.delete()`/`DELETE FROM`/`.remove()`) against any table anywhere in the codebase — every removal +is a soft-delete (`deleted_at`) or a status transition — so this isn't live-impacting today, but is a +latent trap for future/manual/ops-level deletes (e.g. dev data resets, admin cleanup). + +**Cutover / code impact:** no application code change (nothing today triggers a delete on these +tables); no data impact (existing rows unaffected — this only changes what happens on a future +`DELETE`). Company DB at cutover: `ALTER TABLE … DROP CONSTRAINT` + `ADD CONSTRAINT … FOREIGN KEY … +ON DELETE SET NULL` for each of the 5 (Postgres has no in-place "alter constraint action" — must +drop/recreate). ## Appendix — annotated A/B diff (002b Task 6) -> After the final verification pass, paste the old-vs-new schema diff here with each hunk -> annotated with its register entry number. +**Verified:** 2026-07-13 (maintainer sign-off). **Tool:** structured parser +`docs/plans/002-oss-migration/002b-task6/compare_schemas.py` (migra not installed; +falls back per plan). **Dumps:** `pg_dump --schema-only --no-owner --no-privileges +--schema=public` from legacy chain (`legacy/`, PG15) vs root baseline (`nxt-backend/`, +PG15 provisional). **Invocation:** + +```bash +# legacy chain (sequential — one stack at a time) +cd legacy && npx supabase@2.109.1 start +PGPASSWORD=postgres pg_dump "postgresql://postgres:postgres@127.0.0.1:54322/postgres" \ + --schema-only --no-owner --no-privileges --schema=public > /tmp/schema-old.sql +npx supabase@2.109.1 stop + +# baseline +cd .. && npx supabase@2.109.1 start && npx supabase@2.109.1 db reset +PGPASSWORD=postgres pg_dump "postgresql://postgres:postgres@127.0.0.1:54322/postgres" \ + --schema-only --no-owner --no-privileges --schema=public > /tmp/schema-new.sql + +python3 docs/plans/002-oss-migration/002b-task6/compare_schemas.py \ + /tmp/schema-old.sql /tmp/schema-new.sql +``` + +**Assertion:** every structural diff is explained by a register entry. **226 explained** +hunks; **9 script false-positives** (all manually resolved — see table below). **0 real gaps** +after register **#22** `one_platform_operator_org` index was added (2026-07-10). + +### Object inventory (post-normalization) + +| Object | Legacy chain | Baseline | Delta | +|--------|-------------|----------|-------| +| Tables | 57 | 35 | −22 dropped | +| Enum types | 42 | 31 | −11 dropped | +| Functions | 27 | 26 | −8 dropped, +7 new helpers | +| RLS policies (non-parameterized) | 102 | 76 | −26 w/dropped tables, −21 readonly-role | +| Indexes | 55 | 93 | +38 net (register #25/#30 adds, #22 partial unique) | + +Column sets on all 35 kept tables **match** after applying column-adjustment drops/renames. + +### Explained diff summary by register entry + +| Register | Count | What changed | +|----------|------:|--------------| +| #1 | 3 | Drop deprecated directive tables + `batch_commands` view | +| #4 | 1 | Drop `directive_watchdog_sessions` | +| #6 | 3 | Drop Make.com notify functions | +| #7 | 2 | Drop meter credit transfers table + function | +| #8 | 2 | Drop features / member_feature | +| #9 | 1 | Drop TypeORM `migrations` ledger | +| #12 | 4 | Drop device registry tables + function | +| #13 | 3 | Drop payouts module | +| #14 | 10 | Drop pd-hero workflow tables | +| #15 | 1 | Drop autopilot_executions | +| #16 | 4 | Rename directive_batches → meter_command_batches (+ indexes) | +| #16/#22 | 2 | Function renames (batch helper, admin-org check) | +| #21 | 1 | Drop orphan historical_grid helper | +| #22 | 3 | PLATFORM_OPERATOR enum, admin-org redesign, partial unique index | +| #23 | 6 | New rls_org_id_from_* helpers | +| #23 | 10 | append_rls_* function body redesigns | +| #24 | 1 | lock_next_order_and_wallets search_path | +| #25/#30 | 50 | New FK/RLS indexes (incl. one_platform_operator_org counted under #22) | +| #26 | 1 | get_grid_status narrowed return + STABLE | +| #27 | 2 | find_top_spenders / find_energy_topup_revenue fixes | +| #28 | 3 | external_system_enum value trims | +| #29 | 1 | notification_type_enum value trim | +| #31 | 2 | STABLE on remaining RLS helpers | +| #32/#22 | 45 | Policy body rewrites (wrap + function rename) | +| #34 | 5 | ON DELETE SET NULL on 5 latest-pointer FKs | +| drop cascade | 4 | Indexes on dropped tables (name lacks table prefix) | +| register drop | 43 | FKs referencing dropped tables/columns | +| column adjustments | (0 stray) | All column diffs absorbed — no unexplained column mismatches | + +**Not in dumps (by design):** + +- **#2/#3/#5/#6** — parameterized roles/policies/grants (company infra; omitted from baseline) +- **#11** — extension omit/drop (platform defaults vs explicit CREATE EXTENSION) +- **#33** — grants omitted from dump (`--no-privileges`); explicit per-object GRANTs live in + init migration; DEFAULT PRIVILEGES omitted per register +- **auth.users triggers** — outside `--schema=public` scope; present in migration, applied at + `db reset` + +### Nine false-positive script flags (resolved) + +| Item | Register | Notes | +|------|----------|-------| +| 6 × hash-named indexes (`IDX_*`) on `directives` / `member_feature` | #1 / #8 | Index names don't contain table name; script missed drop-cascade attribution | +| `idx_lorawan_batch_status` | #1 | Index on dropped `lorawan_directives` | +| Trigger `…_execution_insert` (old) vs `…_execution_ins` (new) | #16 | Postgres 63-char identifier truncation — same trigger, truncated name in live DB | + +### Cosmetic identifier truncations (non-blocking) + +Postgres truncates identifiers > 63 chars at `db reset` (NOTICE only): + +- Trigger: `append_rls_organization_id_on_meter_command_batch_execution_insert` → `…_ins` +- Index: `idx_metering_hardware_imports_metering_hardware_install_session_id` → truncated -_(empty)_ +Migration source text retains the long names; live objects use truncated names. No functional impact. diff --git a/docs/plans/002-oss-migration/002b-schema-inventory.md b/docs/plans/002-oss-migration/002b-schema-inventory.md new file mode 100644 index 0000000..97a303c --- /dev/null +++ b/docs/plans/002-oss-migration/002b-schema-inventory.md @@ -0,0 +1,222 @@ +# Schema Inventory + +**Companion to:** `002b-database-baseline.md` +**Generated:** 2026-07-08 from local reference DB (`legacy/supabase/migrations`, `npx supabase@2.54.10 db start`) +**Owned schemas:** `public` only (no custom application schemas beyond `public`) + +## Summary + +| Kind | Count | +|------|------:| +| enum | 42 | +| extension | 11 | +| function | 27 | +| role | 3 | +| sequence | 17 | +| storage bucket | 1 | +| table | 57 | +| trigger | 29 | +| view | 4 | +| **Total rows** | **191** | + +Spot-check: **57 tables** in `public` (first migration creates 57 — matches after full chain). + +Sequences: 39 total in `public`; 17 listed (use `nextval()` defaults, not serial-owned); 22 serial-owned sequences omitted. + +Auth-schema triggers on `auth.users` are listed at the end — created by the migration chain but live on a Supabase-managed schema. + +## Register + +| object | kind | schema | bucket | owning capability | notes | +|--------|------|--------|--------|-------------------|-------| +| accounts | table | public | keep | Platform core | RLS enabled; 6 policies | +| agents | table | public | keep | Platform core | RLS enabled; 2 policies | +| api_keys | table | public | keep | Platform core | RLS enabled | +| audits | table | public | keep | (5) Field ops | RLS enabled; 1 policy; user-admin / install audit trail | +| autopilot_executions | table | public | drop | | RLS enabled; register #15 — deferred capability; future microservice | +| bank_accounts | table | public | drop | | RLS enabled; register #13 | +| banks | table | public | keep | (3) Payments | RLS enabled | +| connection_requested_meters | table | public | keep | (2) Metering | RLS enabled; 2 policies | +| connections | table | public | keep | (2) Metering | RLS enabled; 5 policies | +| customers | table | public | keep | (2) Metering | RLS enabled; 4 policies | +| dcus | table | public | keep | Platform core | RLS enabled; 4 policies | +| device_logs | table | public | drop | | RLS enabled; 3 policies; register #12 | +| device_types | table | public | drop | | RLS enabled; 4 policies; register #12 | +| devices | table | public | drop | | RLS enabled; 3 policies; register #12 | +| directive_batch_executions | table | public | keep | (2) Metering | RLS enabled; 2 policies; **rename → `meter_command_batch_executions`** (register #16) | +| directive_batches | table | public | keep | (2) Metering | RLS enabled; 3 policies; **rename → `meter_command_batches`** (register #16); drop `directive_type` (register #1 §1) | +| directive_watchdog_sessions | table | public | drop | | RLS enabled; register #4 | +| directives | table | public | exclude | | RLS enabled; 3 policies | +| energy_cabins | table | public | keep | (1) Production monitoring | RLS enabled; 2 policies; pegasus grid map layer (Supabase read) | +| features | table | public | drop | | RLS enabled; 2 policies; register #8 | +| grids | table | public | keep | Platform core | RLS enabled; 7 policies | +| issues | table | public | keep | (5) Field ops | RLS enabled; 2 policies | +| lorawan_directives | table | public | exclude | | RLS enabled; 3 policies | +| member_feature | table | public | drop | | RLS enabled; register #8 | +| members | table | public | keep | Platform core | RLS enabled; 2 policies | +| meter_commissionings | table | public | keep | (2) Metering | RLS enabled; 2 policies | +| meter_credit_transfers | table | public | exclude | | RLS enabled; register #7 | +| meter_interactions | table | public | keep | (2) Metering | RLS enabled; 2 policies; `batch_execution_id` → `meter_command_batch_executions` (register #16) | +| metering_hardware_imports | table | public | keep | (2) Metering | RLS enabled; 1 policy | +| metering_hardware_install_sessions | table | public | keep | (2) Metering | RLS enabled; 3 policies | +| meters | table | public | keep | (2) Metering | RLS enabled; 5 policies; drop `current_special_status` (register #1 §1); drop `device_id` (register #12 §4) | +| migrations | table | public | drop | | RLS enabled; legacy TypeORM ledger; register #9 | +| mppts | table | public | keep | (1) Production monitoring | RLS enabled; 2 policies | +| notes | table | public | keep | (5) Field ops | RLS enabled; 5 policies | +| notification_parameters | table | public | keep | (4) Notifications | RLS enabled | +| notifications | table | public | keep | (4) Notifications | RLS enabled | +| orders | table | public | keep | (3) Payments | RLS enabled; 5 policies; drop `directive_id`, `lorawan_directive_id` (register #1 §1); drop `meter_credit_transfer_id` (register #7 §2) | +| organizations | table | public | keep | Platform core | RLS enabled; 6 policies | +| payouts | table | public | drop | | RLS enabled; 1 policy; register #13 | +| pd_action_templates | table | public | drop | | RLS enabled; 1 policy; register #14 | +| pd_actions | table | public | drop | | RLS enabled; 3 policies; register #14 | +| pd_audits | table | public | drop | | RLS enabled; 1 policy; register #14 | +| pd_document_templates | table | public | drop | | RLS enabled; 1 policy; register #14 | +| pd_documents | table | public | drop | | RLS enabled; 2 policies; register #14 | +| pd_flow_templates | table | public | drop | | RLS enabled; 1 policy; register #14 | +| pd_flows | table | public | drop | | RLS enabled; 3 policies; register #14 | +| pd_section_templates | table | public | drop | | RLS enabled; register #14 | +| pd_sections | table | public | drop | | RLS enabled; register #14 | +| pd_site_submissions | table | public | keep | (5) Field ops | RLS enabled; 3 policies; public submission form | +| pd_sites | table | public | keep | (5) Field ops | RLS enabled; 3 policies; drop `pd_flow_id` (register #14 §5) | +| poles | table | public | keep | Platform core | RLS enabled; 4 policies | +| routers | table | public | keep | Platform core | RLS enabled; 2 policies | +| solcast_cache | table | public | keep | (1) Production monitoring | RLS enabled | +| transactions | table | public | keep | (3) Payments | RLS enabled; 3 policies | +| ussd_session_hops | table | public | keep | (2) Metering | RLS enabled | +| ussd_sessions | table | public | keep | (2) Metering | RLS enabled | +| wallets | table | public | keep | (3) Payments | RLS enabled; 4 policies | +| agents_with_account | view | public | keep | Platform core | | +| batch_commands | view | public | exclude | | UNION over deprecated directive tables | +| customers_with_account | view | public | keep | (2) Metering | pegasus customers list | +| meters_with_account_and_statuses | view | public | keep | (2) Metering | drop `current_special_status` (register #1 §1); drop `device_id` (register #12 §4) | +| account_type_enum | enum | public | keep | Platform core | values: AGENT, MEMBER, CUSTOMER | +| communication_protocol_enum | enum | public | keep | (2) Metering | values: CALIN_V1, CALIN_V2, CALIN_LORAWAN; enum value trim deferred | +| currency_enum | enum | public | keep | (3) Payments | values: USD, NGN, EUR; shared across capabilities | +| directive_direction_enum | enum | public | exclude | | values: UP, DOWN | +| directive_error_enum | enum | public | exclude | | values: GRID_DOWN, DCU_OFFLINE, NO_METER, NO_DCU, NO_GRID, NO_CONNECTION, NO_CUSTOMER | +| directive_phase_enum | enum | public | exclude | | values: A, B, C | +| directive_special_status_enum | enum | public | exclude | | values: POWER_LIMIT_BREACHED, CREDIT_EXHAUSTED, …; was on `meters.current_special_status` (dropped) | +| directive_status_enum | enum | public | exclude | | values: INITIALISED, PENDING, SENT_TO_API, … | +| directive_type_enum | enum | public | exclude | | values: ON, OFF, READ_VOLTAGE, …; was on `directive_batches.directive_type` (dropped) | +| external_system_enum | enum | public | keep | Platform core | values: STEAMACO, CALIN, …; enum value trim deferred | +| fs_command_type_enum | enum | public | keep | (2) Metering | values: ON, OFF; on `meter_command_batches.fs_command` (renamed) | +| gender_enum | enum | public | keep | (2) Metering | values: MALE, FEMALE; on `customers`; enum value trim deferred | +| generator_type_enum | enum | public | keep | (2) Metering | values: SMALL, LARGE; on `customers`; enum value trim deferred | +| id_document_type_enum | enum | public | keep | (2) Metering | values: PASSPORT, NATIONAL_ID, …; on `customers`; enum value trim deferred | +| issue_status_enum | enum | public | keep | (5) Field ops | values: OPEN, CLOSED, OVERRIDDEN | +| issue_type_enum | enum | public | keep | (5) Field ops | values: NO_COMMUNICATION, METER_NOT_ACTIVATED, … | +| member_type_enum | enum | public | keep | Platform core | values: SUPERADMIN, ADMIN, PARTNER, … | +| meter_commissioning_status_enum | enum | public | keep | (2) Metering | values: PENDING, PROCESSING, SUCCESSFUL, FAILED | +| meter_credit_transfer_status_enum | enum | public | exclude | | values: PENDING, PROCESSING, SUCCESSFUL, FAILED; register #7 | +| meter_interaction_status_enum | enum | public | keep | (2) Metering | values: QUEUED, ABORTED, PROCESSING, … | +| meter_interaction_type_enum | enum | public | keep | (2) Metering | values: READ_CREDIT, TOP_UP, TURN_ON, … | +| meter_phase_enum | enum | public | keep | (2) Metering | values: SINGLE_PHASE, THREE_PHASE | +| meter_type_enum | enum | public | keep | (2) Metering | values: HPS, FS | +| mhi_operation_enum | enum | public | keep | (2) Metering | values: ADD, REMOVE | +| mhi_status_enum | enum | public | keep | (2) Metering | values: PENDING, PROCESSING, SUCCESSFUL, FAILED | +| mppt_type_enum | enum | public | keep | (1) Production monitoring | values: MPPT, PV_INVERTER | +| notification_status_enum | enum | public | keep | (4) Notifications | values: PENDING, PROCESSING, … | +| notification_type_enum | enum | public | keep | (4) Notifications | values: GRID_IS_HPS_ON_STATE_CHANGE, …; enum value trim deferred | +| order_actor_type_enum | enum | public | keep | (3) Payments | values: BANKING_SYSTEM, ORGANIZATION, … | +| order_status_enum | enum | public | keep | (3) Payments | values: INITIALISED, PENDING, COMPLETED, … | +| order_type_enum | enum | public | keep | (3) Payments | values: ENERGY_TOPUP, CONNECTION_PAYMENT, … | +| organization_type_enum | enum | public | keep | Platform core | values: SOLAR_DEVELOPER, LENDER, DATA_AGGREGATOR | +| payment_channel_enum | enum | public | keep | (3) Payments | values: USSD, AYRTON, NIFFLER, TELEGRAM | +| payment_method_enum | enum | public | keep | (3) Payments | values: CREDIT_CARD, USSD, BANK_TRANSFER | +| payout_status_enum | enum | public | drop | | register #13 | +| pd_action_status_enum | enum | public | drop | | register #14 | +| pd_action_type_enum | enum | public | drop | | register #14 | +| pd_document_type_enum | enum | public | drop | | register #14 | +| solcast_cache_request_type_enum | enum | public | keep | (1) Production monitoring | values: ESTIMATED_ACTUALS, FORECAST | +| transaction_status_enum | enum | public | keep | (3) Payments | values: SUCCESSFUL, FAILED | +| wallet_type_enum | enum | public | keep | (3) Payments | values: VIRTUAL, REAL | +| weather_type_enum | enum | public | keep | Platform core | values: CLOUDY, CLOUDS, SHOWERS, SUNNY, … | +| append_rls_organization_id_by_account_id() | function | public | keep | Platform core | member insert trigger | +| append_rls_organization_id_by_connection_id() | function | public | keep | (2) Metering | connections / connection_requested_meters triggers | +| append_rls_organization_id_by_customer_id() | function | public | keep | | shared — connections / customers / notes triggers | +| append_rls_organization_id_by_customer_id_or_agent_id_or_connec() | function | public | keep | (3) Payments | wallets trigger | +| append_rls_organization_id_by_dcu_id_or_meter_id() | function | public | keep | (2) Metering | metering_hardware_install_sessions trigger | +| append_rls_organization_id_by_device_id() | function | public | drop | | register #12 | +| append_rls_organization_id_by_directive_batch_id() | function | public | keep | (2) Metering | **rename → `append_rls_organization_id_by_meter_command_batch_id()`** (register #16) | +| append_rls_organization_id_by_grid_id() | function | public | keep | Platform core | grid-scoped entity inserts | +| append_rls_organization_id_by_historical_grid_id() | function | public | keep | Platform core | orphan — no trigger in chain; review in Task 3c | +| append_rls_organization_id_by_meter_id() | function | public | keep | (5) Field ops | issues trigger | +| append_rls_organization_id_by_metering_hardware_install_session() | function | public | keep | (2) Metering | meter_commissionings trigger | +| append_rls_organization_id_by_order_id() | function | public | keep | (3) Payments | transactions trigger | +| append_rls_organization_id_by_receiver_meter_id() | function | public | exclude | | only used by excluded `meter_credit_transfers` trigger; register #7 | +| find_energy_topup_revenue(grid_id integer, start_date timestamp with time zone, end_date timestamp with time zone) | function | public | keep | (3) Payments | `@core/spending` RPC | +| find_top_spenders(grid_id integer, limit_count integer, start_date timestamp with time zone, end_date timestamp with time zone) | function | public | keep | (3) Payments | `@core/spending` RPC | +| get_grid_status(grid_id integer) | function | public | keep | (1) Production monitoring | pegasus grid dashboard RPC | +| handle_new_user() | function | public | keep | Platform core | auth.users trigger | +| handle_update_user() | function | public | keep | Platform core | auth.users trigger | +| lock_next_order(uuid uuid) | function | public | drop | | register #13 | +| lock_next_order_and_wallets(uuid uuid) | function | public | keep | (3) Payments | tiamat wallets service | +| lock_next_pd_action() | function | public | drop | | loch pd-hero service; register #14 | +| notify_make_about_is_fs_on_updated() | function | public | parameterize | | optional recipe: `docs/database/optional/make-grid-triggers.sql` | +| notify_make_about_is_hps_on_updated() | function | public | parameterize | | optional recipe: `docs/database/optional/make-grid-triggers.sql` | +| notify_make_about_kwh_tariff_essential_service_updated() | function | public | parameterize | | optional recipe: `docs/database/optional/make-grid-triggers.sql` | +| rls_check_if_lender() | function | public | keep | Platform core | RLS helper | +| rls_check_if_nxt_member() | function | public | keep | Platform core | RLS helper | +| rls_get_member_org_id() | function | public | keep | Platform core | RLS helper | +| append_rls_organization_id_on_agent_insert ON agents | trigger | public | keep | Platform core | | +| append_rls_organization_id_on_requested_connection_meters_inser ON connection_requested_meters | trigger | public | keep | (2) Metering | | +| append_rls_organization_id_on_connection_insert ON connections | trigger | public | keep | (2) Metering | | +| append_rls_organization_id_on_customer_insert ON customers | trigger | public | keep | (2) Metering | | +| append_rls_organization_id_on_dcus_insert ON dcus | trigger | public | keep | Platform core | | +| append_rls_organization_id_by_device_id ON device_logs | trigger | public | drop | | register #12 | +| append_rls_organization_id_on_devices_insert ON devices | trigger | public | drop | | register #12 | +| append_rls_organization_id_on_directive_batch_execution_insert ON directive_batch_executions | trigger | public | keep | (2) Metering | **rename → `…_on_meter_command_batch_execution_insert` ON `meter_command_batch_executions`** (register #16) | +| append_rls_organization_id_on_directive_batch_insert ON directive_batches | trigger | public | keep | (2) Metering | **rename → `…_on_meter_command_batch_insert` ON `meter_command_batches`** (register #16) | +| append_rls_organization_id_on_directive_insert ON directives | trigger | public | exclude | | deprecated table | +| append_rls_organization_id_on_energy_cabin_insert ON energy_cabins | trigger | public | keep | (1) Production monitoring | on keep table `energy_cabins` | +| notify_make_about_is_fs_on_updated ON grids | trigger | public | parameterize | | optional recipe: `docs/database/optional/make-grid-triggers.sql` | +| notify_make_about_is_hps_on_updated ON grids | trigger | public | parameterize | | optional recipe: `docs/database/optional/make-grid-triggers.sql` | +| notify_make_about_kwh_tariff_essential_service_updated ON grids | trigger | public | parameterize | | optional recipe: `docs/database/optional/make-grid-triggers.sql` | +| append_rls_organization_id_on_issue_insert ON issues | trigger | public | keep | (5) Field ops | | +| append_rls_organization_id_on_lorawan_directive_insert ON lorawan_directives | trigger | public | exclude | | deprecated table | +| append_rls_organization_id_on_member_insert ON members | trigger | public | keep | Platform core | | +| append_rls_organization_id_on_meter_commissioning_insert ON meter_commissionings | trigger | public | keep | (2) Metering | | +| append_rls_organization_id_on_meter_credit_transfer_insert ON meter_credit_transfers | trigger | public | exclude | | register #7 | +| append_rls_organization_id_on_meter_install_session_insert ON metering_hardware_install_sessions | trigger | public | keep | (2) Metering | | +| append_rls_organization_id_on_meter_insert ON meters | trigger | public | keep | (2) Metering | | +| append_rls_organization_id_on_mppt_insert ON mppts | trigger | public | keep | (1) Production monitoring | | +| append_rls_organization_id_on_note_insert ON notes | trigger | public | keep | (5) Field ops | | +| append_rls_organization_id_on_pole_insert ON poles | trigger | public | keep | Platform core | | +| append_rls_organization_id_on_route_insert ON routers | trigger | public | keep | Platform core | **rename → `append_rls_organization_id_on_router_insert`** (register #10) | +| append_rls_organization_id_on_transaction_insert ON transactions | trigger | public | keep | (3) Payments | | +| append_rls_organization_id_on_wallet_insert ON wallets | trigger | public | keep | (3) Payments | | +| autopilot_executions_id_seq | sequence | public | drop | | register #15 | +| device_logs_id_seq | sequence | public | drop | | register #12 | +| device_types_id_seq | sequence | public | drop | | register #12 | +| devices_id_seq | sequence | public | drop | | register #12 | +| lorawan_directives_id_seq | sequence | public | exclude | | deprecated table | +| meter_interactions_id_seq | sequence | public | keep | (2) Metering | not owned by serial/identity column | +| pd_action_templates_id_seq | sequence | public | drop | | register #14 | +| pd_actions_id_seq | sequence | public | drop | | register #14 | +| pd_audits_id_seq | sequence | public | drop | | register #14 | +| pd_document_templates_id_seq | sequence | public | drop | | register #14 | +| pd_documents_id_seq | sequence | public | drop | | register #14 | +| pd_flow_templates_id_seq | sequence | public | drop | | register #14 | +| pd_flows_id_seq | sequence | public | drop | | register #14 | +| pd_section_templates_id_seq | sequence | public | drop | | register #14 | +| pd_sections_id_seq | sequence | public | drop | | register #14 | +| pd_site_submissions_id_seq | sequence | public | keep | (5) Field ops | not owned by serial/identity column | +| pd_sites_id_seq | sequence | public | keep | (5) Field ops | not owned by serial/identity column | +| grafana_readonly | role | (cluster) | parameterize | | optional recipe: `docs/database/optional/grafana-readonly.sql` (role + grants + 15 RLS policies) | +| make_readonly | role | (cluster) | parameterize | | optional recipe: `docs/database/optional/make-readonly.sql` (role + grants + 4 RLS policies) | +| snaplet_readonly_2 | role | (cluster) | parameterize | | optional recipe: `docs/database/optional/snaplet-readonly.sql` (role + grants on public + auth) | +| hypopg | extension | extensions | drop | | v1.4.1; advisor tooling; register #11 — omit from init | +| index_advisor | extension | extensions | drop | | v0.2.0; advisor tooling; register #11 — omit from init | +| pg_graphql | extension | graphql | drop | | v1.5.9; off by default on new hosted projects; register #11 — omit from init | +| pg_net | extension | extensions | keep | Platform core | v0.14.0; register #11 — **in init** (`IF NOT EXISTS`) | +| pg_stat_statements | extension | extensions | keep | Platform core | v1.10; Supabase default — **omit from init** (register #11) | +| pgcrypto | extension | extensions | keep | Platform core | v1.3; Supabase default — **omit from init** (register #11) | +| pgjwt | extension | extensions | keep | Platform core | v0.2.0; register #11 — **in init** (`IF NOT EXISTS`) | +| pgsodium | extension | pgsodium | keep | Platform core | v3.1.8; register #11 — **in init** (`IF NOT EXISTS`) | +| postgis | extension | extensions | keep | Platform core | v3.3.7; schema-required; register #11 — **in init** (`IF NOT EXISTS`) | +| supabase_vault | extension | vault | keep | Platform core | v0.2.8; Supabase default — **omit from init** (register #11) | +| uuid-ossp | extension | extensions | keep | Platform core | v1.1; Supabase default — **omit from init** (register #11) | +| (none) | storage bucket | storage | keep | Platform core | placeholder row — no buckets in migration chain; not in init | +| on_auth_user_created ON auth.users | trigger | auth | keep | Platform core | migration-owned; Supabase-managed table | +| on_auth_user_updated ON auth.users | trigger | auth | keep | Platform core | migration-owned; Supabase-managed table | diff --git a/docs/plans/002-oss-migration/002b-schema-performance-audit.md b/docs/plans/002-oss-migration/002b-schema-performance-audit.md new file mode 100644 index 0000000..736a740 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-schema-performance-audit.md @@ -0,0 +1,455 @@ +# Schema Performance Audit (working review) + +**Companion to:** `002b-database-baseline.md` — **Task 3d** +**Status:** **Complete 2026-07-10** — D1–D4 all signed off (registers #30, #31, #32; D4 no register entry — no findings) +**Generated:** 2026-07-10 — reference DB (`legacy/supabase/migrations`, `npx supabase@2.54.10 db start`), +queried live (`pg_indexes`, `information_schema`, `pg_policies`) rather than reading migration SQL, since +Docker was available this session. + +## Batch plan (proposed) + +| batch | scope | status | +|-------|-------|--------| +| **D1** | Enumerate indexes on keep tables; FK/RLS coverage gap analysis; redundancy check | **signed off 2026-07-10** — register #30 | +| **D2** | Function volatility (RLS helpers + RPCs + trigger functions) | **signed off 2026-07-10** — register #31 | +| **D3** | RLS policy invocation pattern normalization (bare vs. subquery-wrapped) | **signed off 2026-07-10** — register #32 | +| **D4** | Remaining triggers/views (`append_rls_*` already resolved in 3c — register #23) | **signed off 2026-07-10** — no findings, no register entry | + +## Purpose + +A **structural/static** performance audit of the OSS baseline schema, done once every keep object +(tables, columns, enums, functions, triggers, renames) is locked in from Task 3a–3c, and before Task 5 +authors the init migration / Task 6 diffs it against the old chain — so findings are inputs to the +migration, not a retrofit, and any resulting schema change is registered before the A/B diff runs. + +**Not in scope:** empirical, load-driven tuning (`EXPLAIN ANALYZE` under realistic data volume). There +is no data yet — per this plan's non-goals, "Any data migration — this plan ships schema only." That +kind of tuning belongs to a later capability-import or production-readiness pass, once real usage +exists. + +## Scope + +| concern | seed from | notes | +|---------|-----------|-------| +| Function volatility | `pg_proc.provolatile` on keep functions (Task 3c list) | Legacy chain leaves every function at the implicit default `VOLATILE` — confirmed no `CREATE FUNCTION` declares `STABLE`/`IMMUTABLE`. RLS-attached functions matter most: unmarked, they can be re-evaluated once per row instead of once per statement. | +| RLS policy invocation pattern | `pg_policies` USING/WITH CHECK clauses | Legacy chain is inconsistent: some policies wrap helper calls in a subquery (`( SELECT public.fn() AS fn )`, plan-cacheable), others call bare (`public.fn()`). Distinct from volatility. | +| Indexes on keep tables | **new** — reference DB (`pg_indexes` / `\d` on keep tables) | Not previously inventoried (Task 2's inventory has no "index" kind). Check: FK columns indexed, RLS-predicate columns indexed (e.g. `rls_organization_id`), redundant/overlapping indexes, coverage survives Task 3 renames (`meter_command_batches` etc.). | +| Trigger design overhead | Task 3c H1b (`append_rls_*` group) + H5 (all keep triggers) | Whether the `BEFORE INSERT` denormalization pattern is the right shape, or any function could be simplified/merged. | +| View definitions | Keep views (`002b-schema-inventory.md`) | e.g. `meters_with_account_and_statuses` — wide view, many joins; check plan shape. | + +## Register + +### Function volatility — D2 — **confirmed 2026-07-10, register #31** + +**Method:** enumerated all keep functions from Task 3c's final register (26: 20 legacy-chain keep + +6 new `rls_org_id_from_*` helpers) and cross-checked each against Task 3c's volatility decisions. +**Finding: only 2 functions were left without an explicit volatility decision** — everything else was +already decided during Task 3c (H1b/H1c/H2/H3), confirming those decisions here rather than +re-litigating them. + +#### Already decided in Task 3c — confirmed, not re-litigated + +| function | volatility | register § | where decided | +|----------|-----------|------------|----------------| +| `rls_check_if_admin_org_member()` (renamed from `rls_check_if_nxt_member()`) | `STABLE` | #22 | H1c | +| `rls_org_id_from_grid/customer/connection/agent/meter/dcu()` (6 new helpers) | `STABLE` | #23 | H1b | +| `get_grid_status()` | `STABLE` | #26 | H2 | +| `find_energy_topup_revenue()` | `STABLE` | #27 | H3 | +| `find_top_spenders()` | `STABLE` | #27 | H3 | +| `lock_next_order_and_wallets()` | `VOLATILE` (unmarked, correct) | #24 (search_path only) | H3 — atomic `FOR UPDATE SKIP LOCKED` write, side effects | +| `append_rls_organization_id_by_*()` ×10 trigger functions | `VOLATILE` (unmarked, correct) | #23 | H1b — explicitly decided "no further volatility decision needed"; trigger functions are invoked once per row by the trigger manager, not through query-expression evaluation, so `STABLE`/`IMMUTABLE` marking has no planner effect for them either way | +| `handle_new_user()` | `VOLATILE` (unmarked, correct) | — | H2 — performs `INSERT` into `accounts`, a real side effect | +| `handle_update_user()` | `VOLATILE` (unmarked, correct) | — | H2 — performs `UPDATE` on `accounts`, a real side effect | +| `sync_admin_organization_id_guc()` (new) | `VOLATILE` (unmarked, correct) | #22 | H5c — calls `set_config()`, a session-state side effect | + +#### New findings — D2 — confirmed 2026-07-10, action: mark `STABLE`, register #31 + +| function | current | proposed | rationale | notes | +|----------|---------|----------|-----------|-------| +| `rls_check_if_lender()` | unmarked (`VOLATILE`) | **`STABLE`** | Single `RETURN` reading `auth.jwt() -> 'app_metadata' ->> 'organization_type'` — no side effects, same value for the whole statement (JWT claims are fixed per request). Identical shape/rationale to `rls_check_if_admin_org_member()` (register #22), already `STABLE`. Used in **4 policies** across 4 keep tables (`accounts`, `customers`, `grids`, `orders`). | Already has `SECURITY DEFINER` + `SET search_path TO ''` — spot-checked, no hardening needed | +| `rls_get_member_org_id()` | unmarked (`VOLATILE`) | **`STABLE`** | Single `RETURN` reading `auth.jwt() -> 'app_metadata' ->> 'organization_id'` — no side effects, same value for the whole statement. **The single most-invoked RLS helper in the schema** — used in **~24 policies across ~20 keep tables** (`accounts`, `agents`, `connection_requested_meters`, `connections`, `customers`, `dcus`, `meter_command_batches`, `energy_cabins`, `issues`, `members`, `meter_commissionings`, `metering_hardware_install_sessions`, `meters`, `mppts`, `notes`, `orders`, `organizations`, `poles`, `routers`, `transactions`) — highest-value `STABLE` marking in the whole audit given call volume. | Already has `SECURITY DEFINER` + `SET search_path TO ''` — spot-checked, no hardening needed | + +**Why this matters:** unmarked (`VOLATILE`) functions used in an RLS policy's `USING`/`WITH CHECK` +clause **cannot** be evaluated once per statement — Postgres must treat them as potentially returning a +different value on every row and re-invoke them per row. Marking `STABLE` (safe here — same call, +same result, within one statement) lets the planner cache the result once per statement instead, +which matters most for exactly these two functions given how many rows-per-query and how many +policies they gate. + +### RLS policy invocation pattern — D3 — **confirmed 2026-07-10, register #32** + +**Method:** all 123 `CREATE POLICY` statements live in a single migration file (the initial dump) — no +later migration touches a policy (`ALTER POLICY`/`DROP POLICY` — none found), so parsing the SQL +directly is exact, no DB needed. Extracted every `USING`/`WITH CHECK` clause on the 35 keep tables (91 +of the 123) and looked specifically for calls to the 3 helper functions actually used **inside RLS +policies** (`rls_check_if_nxt_member()` → renamed `rls_check_if_admin_org_member()`, +`rls_check_if_lender()`, `rls_get_member_org_id()`) — the `append_rls_*` functions are **trigger** +functions (already covered in Task 3c H1b), not policy-attached, so out of scope here. + +**Finding:** of 69 helper-function calls across keep-table policies, **51 are already +subquery-wrapped** (`( SELECT public.fn() AS fn )` — the Postgres/Supabase-recommended pattern that +lets the planner treat the call as an `InitPlan`, evaluated once per statement and cached, rather than +re-invoked as part of every row's filter expression) and **18 are bare** (`public.fn()` called +directly). This is exactly the inconsistency the plan flagged. A clean pattern emerged: **every bare +call is on an `INSERT`/`UPDATE` policy — zero are on `SELECT` policies** (all 26 `SELECT`-side +`rls_check_if_nxt_member()`/`rls_check_if_lender()`/`rls_get_member_org_id()` calls are already +wrapped). Looks like whoever wrote the `INSERT`/`UPDATE` policies just used the simpler bare form, +inconsistently with the `SELECT` policies for the same tables/functions. + +**Why it still matters for `INSERT`/`UPDATE`, not just `SELECT`:** a single-row `INSERT` only +evaluates its `WITH CHECK` once regardless of wrapping, so the wrap is "free" there — but a bulk +`UPDATE` (e.g. `UPDATE meters SET … WHERE grid_id = X` touching many rows) re-evaluates a bare +`USING` clause per candidate row, same as `SELECT`. Normalizing removes the inconsistency and closes +that bulk-`UPDATE` gap, and pairs with D2: both functions are about to be marked `STABLE`, which is +what makes the `InitPlan` caching behind the wrap valid in the first place (an unmarked/`VOLATILE` +function can't safely be cached across rows even when wrapped). + +#### Confirmed 2026-07-10 — normalize all 18 bare calls to the wrapped form, register #32 + +| table | policy | clause | function | current | proposed | +|-------|--------|--------|----------|---------|----------| +| `grids` | Allow NXT Grid to insert | `WITH CHECK` | `rls_check_if_admin_org_member()` | bare | `( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member )` | +| `meters` | Allow NXT Grid to insert | `WITH CHECK` | `rls_check_if_admin_org_member()` | bare | wrapped | +| `notes` | Allow NXT Grid to insert | `WITH CHECK` | `rls_check_if_admin_org_member()` | bare | wrapped | +| `organizations` | Allow NXT Grid to insert | `WITH CHECK` | `rls_check_if_admin_org_member()` | bare | wrapped | +| `pd_sites` | Allow NXT Grid to insert | `WITH CHECK` | `rls_check_if_admin_org_member()` | bare | wrapped | +| `poles` | Allow NXT Grid to insert | `WITH CHECK` | `rls_check_if_admin_org_member()` | bare | wrapped | +| `wallets` | Allow NXT Grid to insert | `WITH CHECK` | `rls_check_if_admin_org_member()` | bare | wrapped | +| `accounts` | Allow NXT Grid to update | `USING` | `rls_check_if_admin_org_member()` | bare | wrapped | +| `connections` | Allow NXT Grid to update | `USING` | `rls_check_if_admin_org_member()` | bare | wrapped | +| `grids` | Allow NXT Grid to update | `USING` | `rls_check_if_admin_org_member()` | bare | wrapped | +| `meters` | Allow NXT Grid to update | `USING` | `rls_check_if_admin_org_member()` | bare | wrapped | +| `organizations` | Allow NXT Grid to update | `USING` | `rls_check_if_admin_org_member()` | bare | wrapped | +| `pd_site_submissions` | Allow NXT Grid to update | `USING` | `rls_check_if_admin_org_member()` | bare | wrapped | +| `pd_sites` | Allow NXT Grid to update | `USING` | `rls_check_if_admin_org_member()` | bare | wrapped | +| `notes` | Allow org members to insert | `WITH CHECK` | `rls_get_member_org_id()` | bare | `( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id ) = rls_organization_id` | +| `poles` | Allow org members to insert | `WITH CHECK` | `rls_get_member_org_id()` | bare | wrapped (same form) | +| `accounts` | Allow org members to update | `USING` | `rls_get_member_org_id()` | bare | `( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id ) = organization_id` | +| `orders` | Allow org members to update | `USING` | `rls_get_member_org_id()` | bare | `( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id ) = rls_organization_id` | + +**Already consistent — no change (51 policies):** every `SELECT` policy calling one of these 3 +functions, across all 35 keep tables, is already wrapped. Full list not repeated here — see +`002b-schema-inventory.md`/reference DB `pg_policies` for the untouched 51. + +**Out of scope, no change:** policies with no helper-function call — `true` (Grafana/Make/public +read-only grants, `wallets` "TEMPORARILY OPEN"), plain column comparisons with no function call, and +the `notes` "Allow authors to update their own" policy (a correlated subselect against `accounts`, a +different pattern entirely — not a helper-function call, out of this concern's scope). + +### Indexes — D1 + +**Method:** reference DB was available this session (Docker reachable outside the sandbox) — queried +live rather than parsing migration SQL. Scope: the **35 keep tables** (Task 3a/3b final list, target +names used for the two Task 3 renames). 4 keep **views** have no indexes of their own (rely on base-table +indexes) — not separately enumerated. 22 drop/exclude/parameterize tables' indexes are out of scope +(never reach the init migration). + +- `pg_indexes` on the 35 keep tables → **105 existing indexes** (140 total in the reference DB). +- FK constraints (`information_schema.table_constraints`/`key_column_usage`) cross-referenced against + each index's **leading column** → gap list below. +- A separate sweep for **soft FK columns** (`%_id` columns with no formal `FOREIGN KEY` constraint, the + shape that produced register #25) found no new candidates — the 16 hits are all external/opaque IDs + (`external_id`, Telegram chat/thread IDs, Epicollect IDs) or `wallets.goldring_migration_id` (already + dropping, register #19), not internal join paths. + +#### Existing indexes (103 rows after folding 2 already-registered renames) + +
+Full list — click to expand + +| table | index | action | register § | rationale | notes | +|-------|-------|--------|------------|-----------|-------| +| `accounts` | `PK_5a7a02c20412299d198e097a8fe` | keep | | | | +| `accounts` | `REL_13bf998d703e7d4c7b9a90f4f7` | keep | | | | +| `accounts` | `UQ_0ec8c228e89a95aeb4af9ee3226` | keep | | | | +| `accounts` | `UQ_3ffa9ce30e56b6d2abf0a465f5f` | keep | | | | +| `agents` | `PK_9c653f28ae19c5884d5baf6a1d9` | keep | | | | +| `agents` | `REL_df520decc9e003a843d8edd986` | keep | | | | +| `agents` | `idx_agents_rls_organization_id` | keep | | | | +| `api_keys` | `PK_5c8a79801b44bd27b79228e1dad` | keep | | | | +| `api_keys` | `UQ_e42cf55faeafdcce01a82d24849` | keep | | | | +| `audits` | `PK_b2d7a2089999197dc7024820f28` | keep | | | | +| `banks` | `PK_3975b5f684ec241e3901db62d77` | keep | | | | +| `connection_requested_meters` | `PK_ef90f8d1aa3055757e0ff8e4aa7` | keep | | | | +| `connection_requested_meters` | `idx_connection_requested_meters_rls_organization_id` | keep | | | | +| `connections` | `PK_0a1f844af3122354cbd487a8d03` | keep | | | | +| `connections` | `idx_connection_customer_id` | keep | | | | +| `connections` | `idx_connections_rls_organization_id` | keep | | | | +| `customers` | `PK_133ec679a801fab5e070f73d3ea` | keep | | | | +| `customers` | `REL_ebcc29963874e55053e8ee80be` | keep | | | | +| `customers` | `idx_customer_grid_id` | keep | | | | +| `customers` | `idx_customers_rls_organization_id` | keep | | | | +| `dcus` | `IDX_9c4f5693f52e6e5e51e1ca05af` | keep | | | | +| `dcus` | `PK_82192607fa5119101a78fe53b7d` | keep | | | | +| `dcus` | `REL_9334b587e6e5291f8ccd1b11c4` | keep | | | | +| `dcus` | `idx_dcus_rls_organization_id` | keep | | | | +| `energy_cabins` | `PK_59ecb7965974e45fdc2f243bda7` | keep | | | | +| `energy_cabins` | `idx_energy_cabins_location_geom` | keep | | | | +| `energy_cabins` | `idx_energy_cabins_rls_organization_id` | keep | | | | +| `grids` | `PK_840d40fdcde1e935afd43082aca` | keep | | | | +| `grids` | `UQ_61f08f04c9f0f1d3afd24b9be0b` | keep | | | | +| `issues` | `PK_9d8ecbbeff46229c700f0449257` | keep | | | | +| `issues` | `idx_issues_open` | keep | | | Partial index `WHERE issue_status = 'OPEN'`; leading col `rls_organization_id` | +| `issues` | `idx_issues_rls_organization_id` | keep | | | | +| `members` | `PK_28b53062261b996d9c99fa12404` | keep | | | | +| `members` | `REL_fd9dfb97e21b75fc45d42aa614` | keep | | | | +| `members` | `idx_members_rls_organization_id` | keep | | | | +| `meter_command_batch_executions` (was `directive_batch_executions`) | `PK_9494ab23abdfe4e59f3f29842d5` | keep | | | | +| `meter_command_batch_executions` | `idx_directive_batch_executions_rls_organization_id` → `idx_meter_command_batch_executions_rls_organization_id` | keep + rename | #16 | Align with table rename | `ALTER INDEX … RENAME` at cutover | +| `meter_command_batches` (was `directive_batches`) | `PK_92d424811a3fd49a8020eadb97a` | keep | | | | +| `meter_command_batches` | `idx_directive_batches_rls_organization_id` → `idx_meter_command_batches_rls_organization_id` | keep + rename | #16 | Align with table rename | `ALTER INDEX … RENAME` at cutover | +| `meter_commissionings` | `PK_69fce91302a5dc8dd3db274717b` | keep | | | | +| `meter_commissionings` | `idx_meter_commissionings_rls_organization_id` | keep | | | | +| `meter_interactions` | `idx_meter_interaction_batch_execution_id` | keep | | | Composite `(batch_execution_id, meter_interaction_status)` | +| `meter_interactions` | `idx_meter_interaction_meter_commissioning_id` | keep | | | | +| `meter_interactions` | `idx_meter_interaction_meter_id_extra` | keep | | | Composite `(meter_id, meter_interaction_type, created_at DESC)` | +| `meter_interactions` | `meter_interactions_order_id_key` | keep | | | Unique — 1:1 with `orders` | +| `meter_interactions` | `meter_interactions_pkey` | keep | | | | +| `metering_hardware_imports` | `PK_665b32b395a7675648ed77da4f6` | keep | | | Only index on this table — see Tier 1/2 gaps below | +| `metering_hardware_install_sessions` | `PK_4eb998262e5c773a89dfe3ea0e1` | keep | | | | +| `metering_hardware_install_sessions` | `REL_4eb64f5decb7250cfa993410c1` | keep | | | | +| `metering_hardware_install_sessions` | `REL_be64a09dc738420a409cb96026` | keep | | | | +| `metering_hardware_install_sessions` | `idx_metering_hardware_install_sessions_rls_organization_id` | keep | | | | +| `meters` | `IDX_36d092cd2d89756f62421011bb` | keep | | | Unique `(external_reference, external_system)` | +| `meters` | `PK_0a71b52dbb545fa36efaf070583` | keep | | | | +| `meters` | `REL_5606d6ec5ab568377509edc526` | keep | | | | +| `meters` | `REL_86d4557a79e374b5c55cb3b66d` | keep | | | | +| `meters` | `idx_meter_connection_id` | keep | | | | +| `meters` | `idx_meter_dcu_id` | keep | | | | +| `meters` | `idx_meters_rls_organization_id` | keep | | | | +| `meters` | `meters_device_id_key` | drop (follows column drop) | register #12 / col. adj. §4 | Column `device_id` dropped | Index disappears with the column; no separate DDL | +| `mppts` | `PK_c8a4dc57b932c173f6c579804c0` | keep | | | | +| `mppts` | `idx_mppts_rls_organization_id` | keep | | | | +| `notes` | `PK_af6206538ea96c4e77e9f400c3d` | keep | | | | +| `notes` | `idx_notes_rls_organization_id` | keep | | | | +| `notification_parameters` | `PK_338f36e72b69406bed36e054e09` | keep | | | | +| `notifications` | `IDX_14f6e0badc019bbdd2f66f7e8a` | keep | | | On `notification_status` | +| `notifications` | `PK_6a72c3c0f683f6462415e653c3a` | keep | | | | +| `orders` | `PK_710e2d4957aa5878dfe94e4ac2f` | keep | | | | +| `orders` | `REL_093ca3525311b8a12f6cf6b1c9` | drop (follows column drop) | register #1 / col. adj. §1 | Column `directive_id` dropped | Index disappears with the column; no separate DDL | +| `orders` | `REL_92081072063eaf51300ab6c267` | keep | | | Unique on `ussd_session_id` | +| `orders` | `REL_a53c58bdb5ae0193f17497c81b` | drop (follows column drop) | register #7 / col. adj. §2 | Column `meter_credit_transfer_id` dropped | Index disappears with the column; no separate DDL | +| `orders` | `idx_orders_energy_topup_grid` | keep | | | Partial, see redundancy note below | +| `orders` | `idx_orders_energy_topup_receiver` | keep | | | Partial, see redundancy note below | +| `orders` | `idx_orders_historical_grid_id` | keep | | | | +| `orders` | `idx_orders_meta_receiver_id` | keep | | | | +| `orders` | `idx_orders_order_optimized` | keep | | | On `updated_at DESC` | +| `orders` | `idx_orders_rls_organization_id` | keep | | | | +| `orders` | `idx_orders_rls_sender_receiver` | keep | | | Composite `(meta_sender_type, meta_sender_id, meta_receiver_type, meta_receiver_id)` | +| `organizations` | `PK_6b031fcd0863e3f6b44230163f9` | keep | | | Plus register #22's new partial unique `one_platform_operator_org` (already registered, not re-listed here) | +| `pd_site_submissions` | `idx_pd_site_submissions_location_geom` | keep | | | PostGIS `gist` | +| `pd_site_submissions` | `idx_pd_site_submissions_outline_geom` | keep | | | PostGIS `gist` | +| `pd_site_submissions` | `pd_public_submissions_pkey` | keep | | | | +| `pd_sites` | `idx_pd_sites_location_geom` | keep | | | PostGIS `gist` | +| `pd_sites` | `idx_pd_sites_outline_geom` | keep | | | PostGIS `gist` | +| `pd_sites` | `pd_sites_pkey` | keep | | | | +| `poles` | `PK_1f4336016e8de1d62acb05ce829` | keep | | | | +| `poles` | `UQ_67112e4334d7090a571d2fff42a` | keep | | | | +| `poles` | `idx_poles_location_geom` | keep | | | PostGIS `gist` | +| `poles` | `idx_poles_rls_organization_id` | keep | | | | +| `routers` | `PK_b6d283f1e40d4942dedbc0cb27a` | keep | | | | +| `routers` | `idx_routers_rls_organization_id` | keep | | | | +| `solcast_cache` | `PK_a0b1d6bdbc5ca0056201a1c6dd0` | keep | | | | +| `transactions` | `IDX_5c820aa56824815f8e19484ff5` | keep | | | Composite `(wallet_id, transaction_status, created_at)` | +| `transactions` | `PK_a219afd8dd77ed80f5a862f1db9` | keep | | | | +| `transactions` | `idx_transactions_rls_organization_id` | keep | | | | +| `ussd_session_hops` | `PK_56d984bae21ce6e86848a8a3c04` | keep | | | | +| `ussd_sessions` | `PK_c18f16f36e79b2fb87783476830` | keep | | | | +| `wallets` | `PK_8402e5df5a30a229380e83e4f7e` | keep | | | | +| `wallets` | `REL_6580899a2293de27787376887f` | keep | | | Unique on `customer_id` | +| `wallets` | `REL_8db1f4e4f8122bd25d50ad96b2` | keep | | | Unique on `meter_id` | +| `wallets` | `REL_e63e504d8e35ef37a2c56b75eb` | keep | | | Unique on `connection_id` | +| `wallets` | `REL_f499c61c6d6a0ac3f794d966ed` | keep | | | Unique on `organization_id` | +| `wallets` | `REL_f5782e05e8688f0cfbb5c4a52c` | keep | | | Unique on `agent_id` | +| `wallets` | `idx_wallets_rls_organization_id` | keep | | | | + +
+ +#### Gap findings — new indexes — **confirmed 2026-07-10, register #30** + +Cross-referencing every keep table's FK columns and RLS-predicate columns against the existing index +list above surfaced a systemic pattern: **`rls_organization_id`-style denormalized columns are +consistently indexed (that convention is solid), but plain FK columns on most child tables are not.** +Register #25 (`accounts.organization_id`, found during Task 3c H1b) was the first instance of this +gap; this pass is the systematic sweep the plan called for. + +**Tier 1 — RLS-predicate / already-established-convention gaps (direct analog of register #25) — confirmed, action: add, register #30:** + +| table | column | references | proposed index | rationale | +|-------|--------|------------|-----------------|-----------| +| `grids` | `organization_id` | `organizations.id` | `idx_grids_organization_id` | RLS predicate ("Allow org members to select" uses `organization_id`) **and** FK; every other org-owning/org-scoped table has this indexed — direct analog of register #25 (`accounts.organization_id`). | +| `metering_hardware_imports` | `rls_organization_id` | `organizations.id` | `idx_metering_hardware_imports_rls_organization_id` | RLS predicate — table currently has **zero** non-PK indexes; every sibling keep table indexes its `rls_organization_id` column. | +| `meter_command_batch_executions` | `meter_command_batch_id` (was `directive_batch_id`) | `meter_command_batches.id` | `idx_meter_command_batch_executions_meter_command_batch_id` | Parent-lookup FK (register #16 rename) — no supporting index; needed to join batch executions back to their batch (e.g. batch-level status rollups). | + +**Tier 2 — plain FK-column hygiene (no RLS involvement; standard "index every FK" practice — prevents +seq scans on parent-side updates/deletes and speeds child lookups) — confirmed, action: add, register #30:** + +| table | column | references | proposed index | +|-------|--------|------------|-----------------| +| `agents` | `grid_id` | `grids.id` | `idx_agents_grid_id` | +| `api_keys` | `account_id` | `accounts.id` | `idx_api_keys_account_id` | +| `audits` | `agent_id` | `agents.id` | `idx_audits_agent_id` | +| `audits` | `author_id` | `accounts.id` | `idx_audits_author_id` | +| `audits` | `connection_id` | `connections.id` | `idx_audits_connection_id` | +| `audits` | `customer_id` | `customers.id` | `idx_audits_customer_id` | +| `audits` | `dcu_id` | `dcus.id` | `idx_audits_dcu_id` | +| `audits` | `grid_id` | `grids.id` | `idx_audits_grid_id` | +| `audits` | `member_id` | `members.id` | `idx_audits_member_id` | +| `audits` | `meter_id` | `meters.id` | `idx_audits_meter_id` | +| `audits` | `organization_id` | `organizations.id` | `idx_audits_organization_id` | +| `connection_requested_meters` | `connection_id` | `connections.id` | `idx_connection_requested_meters_connection_id` | +| `dcus` | `grid_id` | `grids.id` | `idx_dcus_grid_id` | +| `energy_cabins` | `grid_id` | `grids.id` | `idx_energy_cabins_grid_id` | +| `meter_command_batches` | `author_id` | `accounts.id` | `idx_meter_command_batches_author_id` | +| `meter_command_batches` | `grid_id` | `grids.id` | `idx_meter_command_batches_grid_id` | +| `meter_commissionings` | `metering_hardware_install_session_id` | `metering_hardware_install_sessions.id` | `idx_meter_commissionings_metering_hardware_install_session_id` | +| `metering_hardware_imports` | `metering_hardware_install_session_id` | `metering_hardware_install_sessions.id` | `idx_metering_hardware_imports_metering_hardware_install_session_id` | +| `metering_hardware_install_sessions` | `author_id` | `accounts.id` | `idx_metering_hardware_install_sessions_author_id` | +| `metering_hardware_install_sessions` | `dcu_id` | `dcus.id` | `idx_metering_hardware_install_sessions_dcu_id` | +| `metering_hardware_install_sessions` | `meter_id` | `meters.id` | `idx_metering_hardware_install_sessions_meter_id` | +| `meters` | `pole_id` | `poles.id` | `idx_meters_pole_id` | +| `mppts` | `grid_id` | `grids.id` | `idx_mppts_grid_id` | +| `notes` | `author_id` | `accounts.id` | `idx_notes_author_id` | +| `notes` | `connection_id` | `connections.id` | `idx_notes_connection_id` | +| `notes` | `customer_id` | `customers.id` | `idx_notes_customer_id` | +| `notes` | `meter_id` | `meters.id` | `idx_notes_meter_id` | +| `notifications` | `account_id` | `accounts.id` | `idx_notifications_account_id` | +| `notifications` | `grid_id` | `grids.id` | `idx_notifications_grid_id` | +| `notifications` | `notification_parameter_id` | `notification_parameters.id` | `idx_notifications_notification_parameter_id` | +| `notifications` | `organization_id` | `organizations.id` | `idx_notifications_organization_id` | +| `orders` | `author_id` | `accounts.id` | `idx_orders_author_id` | +| `orders` | `receiver_wallet_id` | `wallets.id` | `idx_orders_receiver_wallet_id` | +| `orders` | `sender_wallet_id` | `wallets.id` | `idx_orders_sender_wallet_id` | +| `pd_site_submissions` | `organization_id` | `organizations.id` | `idx_pd_site_submissions_organization_id` | +| `pd_sites` | `operations_grid_id` | `grids.id` | `idx_pd_sites_operations_grid_id` | +| `pd_sites` | `organization_id` | `organizations.id` | `idx_pd_sites_organization_id` | +| `poles` | `grid_id` | `grids.id` | `idx_poles_grid_id` | +| `routers` | `grid_id` | `grids.id` | `idx_routers_grid_id` | +| `transactions` | `order_id` | `orders.id` | `idx_transactions_order_id` | +| `ussd_session_hops` | `ussd_session_id` | `ussd_sessions.id` | `idx_ussd_session_hops_ussd_session_id` | +| `ussd_sessions` | `account_id` | `accounts.id` | `idx_ussd_sessions_account_id` | +| `ussd_sessions` | `bank_id` | `banks.id` | `idx_ussd_sessions_bank_id` | +| `ussd_sessions` | `meter_id` | `meters.id` | `idx_ussd_sessions_meter_id` | + +_44 columns across 22 tables._ + +**Tier 3 — edge cases — confirmed 2026-07-10, action: no index (revisit if a read path emerges):** + +| table | column | references | notes | +|-------|--------|------------|-------| +| `members` | `busy_commissioning_id` | `grids.id` | Only ever **written** (`user-admin.service.ts` invite/update member); no query anywhere filters by it. Odd name for a member↔grid FK — looks like a "member is busy with a commissioning at this grid" marker, not a join path. **Proposed: no index** unless a read path emerges. | +| `meters` | `rls_grid_id` | `grids.id` | Formal FK, has an `_fkey` constraint, denormalized alongside `rls_organization_id` on meter assignment (`meters.service.ts`) — but **no RLS policy references it** (only `rls_organization_id` is used in `meters` policies) and no query filters by it. Write-only today. **Proposed: no index** — would be pure write-side overhead for a column with no current read consumer; revisit if a grid-scoped meter query path is added later. | + +**Already covered — not new findings:** + +- `accounts.organization_id` — register #25 already adds `idx_accounts_organization_id`; the gap sweep + re-surfaced it (confirms the sweep is complete) but it's not a new item. +- `pd_sites.pd_flow_id` — FK is unindexed in the reference DB, but the column itself is dropped + (register #14 / column adjustments §5) — moot. + +#### Redundant/overlapping indexes — reviewed, no changes — confirmed 2026-07-10 + +`orders` has two pairs of same-column indexes, one full + one partial: + +| Full index | Partial index | Verdict | +|---|---|---| +| `idx_orders_historical_grid_id` (all rows) | `idx_orders_energy_topup_grid` (`WHERE meta_order_type = 'ENERGY_TOPUP'`) | **Keep both.** Not true redundancy — the partial index is a deliberate smaller/faster index for the hot `ENERGY_TOPUP` reporting path (`find_energy_topup_revenue`, `find_top_spenders`); the full index serves all other order-type lookups by grid. | +| `idx_orders_meta_receiver_id` (all rows) | `idx_orders_energy_topup_receiver` (`WHERE meta_order_type = 'ENERGY_TOPUP'`) | **Keep both.** Same rationale — smaller partial index for the energy-topup-by-receiver hot path. | + +No other overlapping/duplicate index definitions found on any keep table. + +#### Observations (no index action — kept for reference, confirmed 2026-07-10) + +Out of index-audit scope (RLS policy content, not structure) but surfaced while tracing predicate +columns — **not proposing changes** per the plan's non-goal ("RLS policy redesign — policies are +carried over as-is"). Maintainer confirmed 2026-07-10: keep as a documented reference (not a deviation +register entry — nothing ships/changes here, so nothing to register per the register's own rule that +every row is a deviation). + +- `meter_interactions` and `wallets` each have an `"Allow org members to select"` policy with `qual = + true` — i.e. **no org-scoping filter at all** for authenticated users on SELECT (relies on the app + layer / other policies). Pre-existing legacy design; not changed here. +- `notifications` has **zero** RLS policies in the reference DB despite `RLS enabled` — effectively + deny-all for the default role; app access presumably goes through the service-role key (bypasses + RLS). Pre-existing; not changed here. + +
+ +### Triggers / views — D4 — **confirmed 2026-07-10, no findings** + +**Method:** enumerated every keep trigger and keep view from `002b-schema-inventory.md`, read each +definition in the reference migration (`legacy/supabase/migrations/20251028091645_remote_schema.sql`), +and checked (a) whether the `append_rls_*` design pattern is the right shape, (b) whether any other +trigger has design overhead worth flagging, (c) whether keep-view join columns are index-covered, and +(d) whether any view's query shape (subqueries, correlated lookups) is a structural concern. + +#### Triggers — no new findings, everything already resolved + +Every keep trigger falls into one of two groups, both already fully reviewed: + +| group | triggers | resolved in | +|-------|----------|-------------| +| `append_rls_*` denormalization (`BEFORE INSERT`) | 18 triggers across `agents`, `connection_requested_meters`, `connections`, `customers`, `dcus`, `energy_cabins`, `issues`, `members`, `meter_command_batch_executions`, `meter_command_batches`, `meter_commissionings`, `meters`, `metering_hardware_install_sessions`, `mppts`, `notes`, `poles`, `routers`, `transactions`, `wallets` | Task 3c H1b — design pattern, function list, and volatility all decided there; register #23 | +| `auth.users` sync (`AFTER INSERT`/`AFTER UPDATE`) | `on_auth_user_created` → `handle_new_user()`, `on_auth_user_updated` → `handle_update_user()` | Task 3c H2 (functions kept, correctly `VOLATILE`) | + +Spot-checked the `auth.users` group's design since it's outside the `append_rls_*` family flagged by the +plan: both are single-statement (`INSERT`/`UPDATE` on `public.accounts`), and `handle_update_user()`'s +`WHERE supabase_id = NEW.id` hits `accounts.supabase_id`, which already has a `UNIQUE` constraint +(`REL_13bf998d703e7d4c7b9a90f4f7`, in the D1 existing-index list) — indexed, no gap. No design overhead +to flag; nothing to change. + +**No triggers outside these two groups exist among keep tables** — confirms the plan's note that +`append_rls_*` was "already resolved in 3c" was the only design-pattern trigger group, and closes D4's +trigger half with zero new findings. + +#### Views — no new findings, join columns already index-covered + +3 keep views (`agents_with_account`, `customers_with_account`, `meters_with_account_and_statuses`) — +the 4th, `batch_commands`, is excluded (deprecated `UNION`, register #7-adjacent). + +| view | joins | index coverage | +|------|-------|-----------------| +| `agents_with_account` | `agents ⋈ accounts` on `agents.account_id = accounts.id` | `accounts.id` is the PK (auto-indexed); `agents.account_id` has `REL_df520decc9e003a843d8edd986` (unique, D1 existing-index list) | +| `customers_with_account` | `customers ⋈ accounts` on `account_id`; correlated subquery `string_agg` over `connections ⋈ meters` filtered by `conn.customer_id = c.id` | `customers.account_id` → `REL_ebcc29963874e55053e8ee80be` (unique); correlated subquery's filter column `connections.customer_id` → `idx_connection_customer_id` (both D1 existing-index list) | +| `meters_with_account_and_statuses` | 7-table join chain: `meters ⋈ connections ⋈ customers ⋈ accounts`, plus 3 `LEFT JOIN`s (`metering_hardware_install_sessions`, `metering_hardware_imports`, `meter_commissionings`) and 1 (`issues`) | every join target is either a PK (auto-indexed: `connections.id`, `customers.id`, `accounts.id`, `metering_hardware_install_sessions.id`, `metering_hardware_imports.id`, `meter_commissionings.id`, `issues.id`) or already covered by a D1-confirmed FK index (`meters.connection_id` → `idx_meter_connection_id`, existing) | + +All three views' join predicates are already index-covered — by pre-existing indexes or by the D1 +sweep (register #30) — so there's no index gap specific to view usage. `customers_with_account`'s +correlated subquery is a legacy design choice (could be a `LEFT JOIN` + `array_agg`/`GROUP BY` instead +of a per-row correlated subquery) but it's driven by an indexed column, and per the plan's non-goal +("view definitions carried over as-is" — no redesign scope, same posture as the D1 RLS-policy-content +observations) — **noting, not changing.** + +**No index or design action for D4.** All keep triggers and views checked out clean against the +concerns the plan raised. + +**Workflow:** + +1. ~~Enumerate indexes on keep tables from the reference DB (new — no prior inventory).~~ **D1 signed off + 2026-07-10 — register #30** (see Indexes section above). +2. ~~Audit function volatility against Task 3c's final keep-function list.~~ **D2 signed off 2026-07-10 + — register #31.** +3. ~~Audit RLS policy invocation patterns (`pg_policies`) for the bare-vs-subquery inconsistency.~~ **D3 + signed off 2026-07-10 — register #32.** +4. ~~Review trigger design (H1b group) and keep-view definitions.~~ **D4 signed off 2026-07-10 — no + findings, nothing to change** (see Triggers / views section above). +5. ~~Sync any resulting drop/add/change to `002b-schema-deviation-register.md` (new **Performance + adjustments** section) with cutover implications.~~ **Done — registers #30, #31, #32 (D1–D3); D4 had + nothing to sync.** +6. **Task 3d complete** — every batch above has a final action; register is synced. + +**D1 sign-off checklist — all confirmed by maintainer 2026-07-10:** + +- [x] Tier 1 (3 indexes) — RLS-predicate gaps, direct analog of register #25 — **add, register #30** +- [x] Tier 2 (44 indexes across 22 tables) — plain FK-column hygiene — **add, register #30** +- [x] Tier 3 edge cases (2 columns) — **no index** (default recommendation confirmed) +- [x] Naming convention `idx__` for all new indexes (matches existing convention) — confirmed +- [x] Redundant-index review (orders partial/full pairs) — **keep both, no change** — confirmed +- [x] Observations (RLS `qual = true` on `meter_interactions`/`wallets`; zero policies on + `notifications`) — **kept as documented reference only**; not a register entry (no schema + change), not a separate follow-up right now +- [x] 2 already-registered index renames (#16) and 3 already-registered index removals (via column + drops #1/#7/#12) — no new action needed, listed for completeness + +**D1 complete.** Register #30 added to `002b-schema-deviation-register.md` **Performance adjustments** +section (47 new indexes: 3 Tier 1 + 44 Tier 2). diff --git a/docs/plans/002-oss-migration/002b-schema-programmability-review.md b/docs/plans/002-oss-migration/002b-schema-programmability-review.md new file mode 100644 index 0000000..c3957fb --- /dev/null +++ b/docs/plans/002-oss-migration/002b-schema-programmability-review.md @@ -0,0 +1,582 @@ +# Schema Programmability Review (working review) + +**Companion to:** `002b-database-baseline.md` — **Task 3c** +**Status:** **Task 3c complete** — H1–H5 signed off (2026-07-09) +**Generated:** 2026-07-09 — full migration chain (`legacy/supabase/migrations/`, 19 files) + +## Summary + +| kind | count | signed off | +|------|------:|------------| +| enum values (31 types + 1 new) | 180 | **H4 signed off** (4 enum value drops total: #28, #29) | +| functions | 20 + 6 new helpers | **signed off** | +| triggers (19 public + 2 auth + 1 new) | 22 | **signed off** | + +Pre-filled renames from register **#10** (router trigger) and **#16** (meter command batch function + triggers). +Default action is **keep** until a batch is signed off; ⚠️ rows need explicit maintainer decision. + +## Review batches + +| batch | scope | status | +|-------|-------|--------| +| **H1** | RLS helpers — split into H1a / H1b / H1c (below) | **signed off** 2026-07-09 | +| **H2** | Auth + platform RPCs (`handle_*`, `get_grid_status`) | **signed off** 2026-07-09 | +| **H3** | Payments RPCs (`lock_next_order_and_wallets`, `find_*`) | **signed off** 2026-07-09 | +| **H4** | Enum value trim (by capability) | **signed off** 2026-07-09 | +| H5 | Triggers vs functions | **signed off** 2026-07-09 | + +## Batch H1 — RLS helpers + +H1 is split so difficult functions are reviewed **one at a time**: + +| sub-batch | scope | status | +|-----------|-------|--------| +| **H1a** | Quick decisions (lender keep, orphan drop, batch rename) | **signed off** 2026-07-09 | +| **H1b** | `append_rls_*` functions + their INSERT triggers | **signed off** 2026-07-09 — reviewed as **one group discussion** (exception to per-function review); consolidated onto shared helpers | +| **H1c** | `rls_check_if_nxt_member()` | **signed off** 2026-07-09 — rename + redesign | +| H1d | `rls_get_member_org_id()` | provisional keep (not flagged) | + +### H1a — Signed off 2026-07-09 + +| # | function | decision | register | +|---|----------|----------|----------| +| 2 | `rls_check_if_lender()` | **keep** | — | +| 3 | `append_rls_organization_id_by_historical_grid_id()` | **drop** | #21 | +| 4 | `append_rls_organization_id_by_directive_batch_id()` | **rename** → `append_rls_organization_id_by_meter_command_batch_id()` | #16 | + +### H1b — `append_rls_*` + triggers — signed off 2026-07-09 + +**Pattern:** each function is a `BEFORE INSERT` trigger handler that denormalizes `grids.organization_id` (or `accounts.organization_id`) into `NEW.rls_organization_id` on child rows, so org-scoped RLS policies can compare against `rls_get_member_org_id()` without joins at read time. All 10 share this one shape — they differ only in *which* join path reaches `grids`/`accounts` from the inserted row — so they're reviewed together rather than one at a time. + +**Gap:** `orders` has `rls_organization_id` but **no** INSERT trigger — the app sets it in tiamat `order-meta.ts`. Not in this group (no `append_rls_*` function wired to it). + +**Decision — keep the denormalization strategy, consolidate the join logic (register #23):** + +1. **Dead join removed** — `by_account_id()` joined `organizations` without using any of its columns; now a plain single-table `SELECT`. +2. **Second dead lookup removed** — the `organization_id` branch of `by_customer_id_or_agent_id_or_connec()` did `SELECT id FROM organizations WHERE id = NEW.organization_id` just to pass the same value back; now assigns `NEW.organization_id` directly. +3. **`SET search_path TO ''` added to all 10** (9 were missing it; addresses the Supabase "Function Search Path Mutable" linter warning). +4. **Join logic consolidated onto 6 new shared helper functions** — the 10 functions repeat the same multi-hop chains (e.g. `meter → connection → customer → grid → organization` appears near-verbatim in 3 of them). Each helper resolves one entity → `organization_id`, delegating up the hierarchy; each trigger function becomes a thin wrapper. + +| Helper (new) | Resolves | Delegates to | +|---|---|---| +| `rls_org_id_from_grid(grid_id)` | `grids.organization_id` | — (leaf) | +| `rls_org_id_from_customer(customer_id)` | `customers.grid_id` → | `rls_org_id_from_grid` | +| `rls_org_id_from_connection(connection_id)` | `connections.customer_id` → | `rls_org_id_from_customer` | +| `rls_org_id_from_agent(agent_id)` | `agents.grid_id` → | `rls_org_id_from_grid` | +| `rls_org_id_from_meter(meter_id)` | `meters.connection_id` → | `rls_org_id_from_connection` | +| `rls_org_id_from_dcu(dcu_id)` | `dcus.grid_id` → | `rls_org_id_from_grid` | + +All six: `LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO ''`. `LANGUAGE sql` + `STABLE` single-`SELECT` functions are inlining-eligible in Postgres — the planner can fold them into the calling query, so delegation costs nothing at runtime versus writing the join inline. + +| # | function | trigger(s) ON table | resolves via | +|---|----------|---------------------|-----------| +| 1 | `append_rls_organization_id_by_account_id()` | `…_on_member_insert` ON `members` | `accounts.organization_id` directly (no helper — dead join removed) | +| 2 | `append_rls_organization_id_by_grid_id()` | `…_on_agent_insert` ON `agents`; `…_on_customer_insert` ON `customers`; `…_on_dcus_insert` ON `dcus`; `…_on_directive_batch_insert` ON `directive_batches`¹; `…_on_energy_cabin_insert` ON `energy_cabins`; `…_on_mppt_insert` ON `mppts`; `…_on_pole_insert` ON `poles`; `…_on_route_insert` ON `routers`² | `rls_org_id_from_grid(NEW.grid_id)` | +| 3 | `append_rls_organization_id_by_connection_id()` | `…_on_requested_connection_meters_inser` ON `connection_requested_meters`; `…_on_meter_insert` ON `meters` | `rls_org_id_from_connection(NEW.connection_id)` | +| 4 | `append_rls_organization_id_by_customer_id()` | `…_on_connection_insert` ON `connections`; `…_on_note_insert` ON `notes` | `rls_org_id_from_customer(NEW.customer_id)` | +| 5 | `append_rls_organization_id_by_customer_id_or_agent_id_or_connec()` | `…_on_wallet_insert` ON `wallets` | branches → `rls_org_id_from_customer` / `rls_org_id_from_agent` / `rls_org_id_from_connection` / `NEW.organization_id` direct / `rls_org_id_from_meter` | +| 6 | `append_rls_organization_id_by_dcu_id_or_meter_id()` | `…_on_meter_install_session_insert` ON `metering_hardware_install_sessions` | branches → `rls_org_id_from_dcu` / `rls_org_id_from_meter` | +| 7 | `append_rls_organization_id_by_directive_batch_id()`¹ | `…_on_directive_batch_execution_insert` ON `directive_batch_executions` | `rls_org_id_from_grid(grid_id)` looked up from batch row | +| 8 | `append_rls_organization_id_by_meter_id()` | `…_on_issue_insert` ON `issues` | `rls_org_id_from_meter(NEW.meter_id)` | +| 9 | `append_rls_organization_id_by_metering_hardware_install_session()` | `…_on_meter_commissioning_insert` ON `meter_commissionings` | `rls_org_id_from_meter(meter_id)` looked up from session row | +| 10 | `append_rls_organization_id_by_order_id()` | `…_on_transaction_insert` ON `transactions` | `rls_org_id_from_grid(historical_grid_id)` looked up from order row | + +¹ Register #16 table/column/function/trigger renames apply at Task 5 (function #7, trigger on row #2 and #7). +² Register #10 trigger rename `…_on_route_insert` → `…_on_router_insert` (row #2). + +**Related finding (out of this group, fixed alongside it):** `accounts` has two RLS policies (`Allow org member to select`, `Allow org members to update`) filtering directly on `organization_id`, but — unlike every other table with an RLS-filtered org column — there's no supporting index. Adding `idx_accounts_organization_id` (register #25). + +**Volatility:** the 6 new helpers are marked `STABLE` (see above). The 10 trigger functions themselves stay unmarked/`VOLATILE` — they run once per row on `BEFORE INSERT`, so volatility marking doesn't apply the same way it did for `rls_check_if_admin_org_member()` (an RLS-policy expression evaluated per-statement). No further volatility decision needed here; general volatility audit for the *rest* of the schema's functions stays in **Task 3d**. + +**Index coverage — checked, no gap found:** traced the join chain for all 10 functions; every hop resolves via a primary-key lookup (walk from a known child row up through FK values to parent PKs), which is always indexed automatically. No additional indexes needed for these functions' own joins. + +### H1c — `rls_check_if_nxt_member()` — signed off 2026-07-09 + +**Decision:** rename + redesign, not a plain rename. The admin-organization concept becomes **DB-native** +instead of the hard-coded literal `2` — see **ADR-007 Amendment (2026-07-09)** for the full +architecture (this was a genuine cross-cutting decision, not just a Task 3c row action). + +| Aspect | Before | After | +|--------|--------|-------| +| Function name | `rls_check_if_nxt_member()` | `rls_check_if_admin_org_member()` | +| Org identification | Hard-coded `nxt_org_id int := 2` | Reads `current_setting('app.admin_organization_id', true)`, populated from `organizations.organization_type = 'PLATFORM_OPERATOR'` via a trigger-maintained GUC | +| Volatility | (unmarked → `VOLATILE`) | Marked **`STABLE`** — evaluated once per statement, not once per row | +| New schema objects | — | `organization_type_enum` value `PLATFORM_OPERATOR`; partial unique index `one_platform_operator_org`; sync trigger on `organizations` | + +**Why not a live `organizations` lookup:** would require an index/heap fetch + MVCC check per +evaluation — categorically slower than a GUC's in-memory read, even with `STABLE`. The GUC is +trigger-maintained so it's always consistent with the DB-native flag without paying that cost at +RLS-check time. + +**Setup flow (no manual script needed for the common path):** create the platform-operator +organization, set `organization_type = 'PLATFORM_OPERATOR'` — the trigger records its id into the GUC +automatically. + +**Open items carried to ADR-007** (not blocking Task 3c, but tracked there): how backend `getConfig()` +consumers and frontend apps resolve this now-DB-native value. + +### H1 function register + +| function | action | register § | rationale | notes | +|----------|--------|------------|-----------|-------| +| `rls_check_if_nxt_member()` | **rename + redesign** | **#22** | H1c signed off 2026-07-09 | → `rls_check_if_admin_org_member()`; reads GUC instead of hard-coded `2`; marked `STABLE`. See ADR-007 Amendment (2026-07-09) | +| `rls_check_if_lender()` | keep | | H1a signed off 2026-07-09 | JWT organization_type = LENDER; 4 lender SELECT policies | +| `rls_get_member_org_id()` | keep | | provisional | JWT organization_id; primary org-scoped RLS helper | +| `append_rls_organization_id_by_account_id()` | **keep + redesign** | **#23** | H1b signed off 2026-07-09 | dead join removed; body now direct `SELECT` (no helper needed) | +| `append_rls_organization_id_by_grid_id()` | **keep + redesign** | **#23** | H1b signed off 2026-07-09 | delegates to new helper `rls_org_id_from_grid()` | +| `append_rls_organization_id_by_historical_grid_id()` | **drop** | **#21** | H1a signed off 2026-07-09 | Orphan — no trigger; dead code | +| `append_rls_organization_id_by_connection_id()` | **keep + redesign** | **#23** | H1b signed off 2026-07-09 | delegates to new helper `rls_org_id_from_connection()` | +| `append_rls_organization_id_by_customer_id()` | **keep + redesign** | **#23** | H1b signed off 2026-07-09 | delegates to new helper `rls_org_id_from_customer()` | +| `append_rls_organization_id_by_customer_id_or_agent_id_or_connec()` | **keep + redesign** | **#23** | H1b signed off 2026-07-09 | branches delegate to helpers; 2nd dead lookup (`organization_id` branch) removed | +| `append_rls_organization_id_by_dcu_id_or_meter_id()` | **keep + redesign** | **#23** | H1b signed off 2026-07-09 | branches delegate to `rls_org_id_from_dcu()` / `rls_org_id_from_meter()` | +| `append_rls_organization_id_by_directive_batch_id()` | **rename + redesign** | **#16, #23** | H1a + H1b signed off 2026-07-09 | → `append_rls_organization_id_by_meter_command_batch_id()`; delegates to `rls_org_id_from_grid()` | +| `append_rls_organization_id_by_meter_id()` | **keep + redesign** | **#23** | H1b signed off 2026-07-09 | delegates to new helper `rls_org_id_from_meter()` | +| `append_rls_organization_id_by_metering_hardware_install_session()` | **keep + redesign** | **#23** | H1b signed off 2026-07-09 | delegates to `rls_org_id_from_meter()` via session's `meter_id` | +| `append_rls_organization_id_by_order_id()` | **keep + redesign** | **#23** | H1b signed off 2026-07-09 | delegates to `rls_org_id_from_grid()` via `historical_grid_id` | +| `rls_org_id_from_grid(grid_id)` | **add (new)** | **#23** | H1b signed off 2026-07-09 | `LANGUAGE sql STABLE`; leaf helper — `grids.organization_id` | +| `rls_org_id_from_customer(customer_id)` | **add (new)** | **#23** | H1b signed off 2026-07-09 | `LANGUAGE sql STABLE`; delegates to `rls_org_id_from_grid()` | +| `rls_org_id_from_connection(connection_id)` | **add (new)** | **#23** | H1b signed off 2026-07-09 | `LANGUAGE sql STABLE`; delegates to `rls_org_id_from_customer()` | +| `rls_org_id_from_agent(agent_id)` | **add (new)** | **#23** | H1b signed off 2026-07-09 | `LANGUAGE sql STABLE`; delegates to `rls_org_id_from_grid()` | +| `rls_org_id_from_meter(meter_id)` | **add (new)** | **#23** | H1b signed off 2026-07-09 | `LANGUAGE sql STABLE`; delegates to `rls_org_id_from_connection()` | +| `rls_org_id_from_dcu(dcu_id)` | **add (new)** | **#23** | H1b signed off 2026-07-09 | `LANGUAGE sql STABLE`; delegates to `rls_org_id_from_grid()` | + +--- + +## Batch H2 — Auth + platform RPCs — signed off 2026-07-09 + +Reviewed one function at a time (same workflow as H1c). + +| # | function | decision | register | +|---|----------|----------|----------| +| 1 | `handle_new_user()` | **keep** | — | +| 2 | `handle_update_user()` | **keep** | — | +| 3 | `get_grid_status(grid_id)` | **keep + redesign** | **#26** | + +### H2 #1 — `handle_new_user()` — signed off 2026-07-09 + +`AFTER INSERT` on `auth.users` → creates bare `public.accounts` row (`supabase_id`, `email`, `phone`, `full_name`, `telegram_link_token`). `organization_id` intentionally deferred to `handle_update_user()` (backend always follows with `updateUserById({ app_metadata })`). Already `SECURITY DEFINER` + `SET search_path TO ''`. No changes. + +### H2 #2 — `handle_update_user()` — signed off 2026-07-09 + +`AFTER UPDATE` on `auth.users` → syncs `email`, `phone`, `full_name`, `organization_id` (from `raw_app_meta_data`) to `accounts`. `organization_id` denormalization required for org-scoped RLS policies on `accounts` (`Allow org member to select/update`). `account_type` / `member_type` / `grid_id` stay JWT-only (RLS helpers read from `auth.jwt()`). Already hardened. No changes. + +### H2 #3 — `get_grid_status(grid_id)` — signed off 2026-07-09 + +RPC for pegasus grid dashboard (`GridDashboardView.vue`). **Redesign** to align with register **#17** column drops: + +| Column | Action | +|--------|--------| +| `is_hps_on`, `is_fs_on`, `is_cabin_meter_credit_depleting`, `customer_count` | **keep** in return type | +| `are_all_dcus_online`, `are_all_dcus_under_high_load_threshold` | **drop** from return type — columns dropped from `grids` (register #17); pegasus already fetches gateway status via separate `dcus` query; UI usage of RPC columns commented out | + +Also marked **`STABLE`** (read-only RPC). Not `SECURITY DEFINER` — runs as caller; RLS on joined tables scopes `customer_count` correctly. + +**Cutover / code impact:** pegasus supabase types + `GridDashboardView` at production-monitoring import (no live UI reads the dropped columns). + +--- + +## Batch H4 — Enum value trim + +Sub-batches by capability (mirrors Task 3a/3b). Default action is **keep** unless signed off otherwise. + +### H4a — Platform core — signed off 2026-07-09 + +| enum | decision | register | +|------|----------|----------| +| `account_type_enum` (3 values) | **keep all** | — | +| `organization_type_enum` (4 values incl. `PLATFORM_OPERATOR`) | **keep all** | #22 (`PLATFORM_OPERATOR` add) | +| `weather_type_enum` (6 values) | **keep all** | — | +| `member_type_enum` (10 values) | **keep all** | — | +| `external_system_enum` | **keep 11, drop 3** | **#28** | + +**`member_type_enum` — keep all 10:** RBAC vocabulary on `members` + JWT `app_metadata`; frontend assigns a subset but DB/validation accepts full set. Trimming = product decision + painful Postgres enum surgery if rows exist. + +**`external_system_enum` — drop `JOTFORM`, `STEAMACO`, `ACREL`; keep rest (incl. `JIRA`):** + +| Value | Decision | Rationale | +|-------|----------|-----------| +| CALIN, VICTRON, FLUTTERWAVE, AFRICASTALKING, TELEGRAM, ZEROTIER, SENDGRID, EPICOLLECT, MAKE, FLOW_XO, SOLCAST, **JIRA** | keep | Live OSS integrations; **JIRA** required — `issues.external_tracking_system` (renamed from `external_system`, register #20) is typed `external_system_enum` with DB default `'JIRA'` | +| JOTFORM | **drop** | pd-hero dropped (#14); no code refs | +| STEAMACO | **drop** | No live code refs (legacy FDW only) | +| ACREL | **drop** | No code refs | + +### H4b — Metering — signed off 2026-07-09 + +**Decision: keep all — no drops.** + +| enum | values | decision | +|------|--------|----------| +| `communication_protocol_enum` | CALIN_V1, CALIN_V2, CALIN_LORAWAN | **keep all** — live CALIN integration paths | +| `gender_enum` | MALE, FEMALE | **keep all** — `customers`, DTO validation | +| `generator_type_enum` | SMALL, LARGE | **keep all** — `customers.generator_owned` | +| `id_document_type_enum` | PASSPORT, NATIONAL_ID, DRIVING_LICENSE, VOTERS_CARD | **keep all** — `connections.document_type`, Epicollect import | +| `meter_commissioning_status_enum`, `meter_interaction_*`, `meter_phase_enum`, `meter_type_enum`, `mhi_*`, `fs_command_type_enum` | all | **keep all** — core metering vocabulary | + +### H4c — Payments — signed off 2026-07-09 + +**Decision: keep all — no drops.** All payment enums (`currency_enum`, `order_*`, `payment_*`, `transaction_status_enum`, `wallet_type_enum`) — ~35 values; no trim flags from Task 3a. + +### H4d — Notifications — signed off 2026-07-09 + +**Decision: keep 15, drop 1.** + +| Value | Decision | Rationale | +|-------|----------|-----------| +| All except below | **keep** | Active notification paths (sendgrid, tiamat orders, loch revenue) or retained for historical `notifications` rows / external automation | +| `AUTO_PAYOUT_GENRATION_REPORT` | **drop** | **#29** — payouts module dropped (register #13); only producer was dropped `payouts.service` | + +### H4e — Field ops + production monitoring — signed off 2026-07-09 + +**Decision: keep all — no drops.** `issue_status_enum`, `issue_type_enum` (22 values), `mppt_type_enum`, `solcast_cache_request_type_enum`. + +--- + +## Batch H3 — Payments RPCs — signed off 2026-07-09 + +Reviewed one function at a time (same workflow as H2). + +| # | function | decision | register | +|---|----------|----------|----------| +| 1 | `lock_next_order_and_wallets(uuid)` | **keep** | **#24** (search_path only) | +| 2 | `find_energy_topup_revenue(...)` | **keep + STABLE** | **#27** | +| 3 | `find_top_spenders(...)` | **keep + STABLE + GROUP BY fix** | **#27** | + +### H3 #1 — `lock_next_order_and_wallets(uuid)` — signed off 2026-07-09 + +Atomic pessimistic lock for tiamat payment loop: oldest `PENDING` order with both wallets unlocked → `FOR UPDATE SKIP LOCKED` on order + sender/receiver wallets → sets `lock_session`, returns wallet IDs. Supersedes dropped `lock_next_order()` (register #13). Logic confirmed sound; correctly stays **`VOLATILE`**. `SET search_path TO ''` already recorded (#24). + +### H3 #2 — `find_energy_topup_revenue(grid_id, start_date, end_date)` — signed off 2026-07-09 + +Read-only revenue aggregate on completed `ENERGY_TOPUP` orders by `historical_grid_id`. Used by loch/yeti via `SpendingService`. Already has `SET search_path TO ''`. Mark **`STABLE`**. No body change. + +### H3 #3 — `find_top_spenders(...)` — signed off 2026-07-09 + +Read-only top-N energy-topup spenders for loch revenue reports. Already has `SET search_path TO ''`. Mark **`STABLE`**. **GROUP BY fix:** legacy grouped by `meta_receiver_id` (meter) + customer fields, splitting one customer across multiple meters into separate rows; init migration uses `GROUP BY meta_receiver_id_part_2, meta_receiver_name_part_2` only (customer-level aggregation). Return columns unchanged (`full_name`, `id`, `amount`). + +--- + +## Register — enum values (H4) + +| enum | value | capability | action | register § | rationale | notes | +|------|-------|------------|--------|------------|-----------|-------| +| account_type_enum | AGENT | Platform core | keep | | | | +| account_type_enum | MEMBER | Platform core | keep | | | | +| account_type_enum | CUSTOMER | Platform core | keep | | | | +| member_type_enum | SUPERADMIN | Platform core | keep | | H4a 2026-07-09 | RBAC vocabulary — keep all 10 | +| member_type_enum | ADMIN | Platform core | keep | | H4a 2026-07-09 | RBAC vocabulary — keep all 10 | +| member_type_enum | PARTNER | Platform core | keep | | H4a 2026-07-09 | RBAC vocabulary — keep all 10 | +| member_type_enum | FINANCE | Platform core | keep | | H4a 2026-07-09 | RBAC vocabulary — keep all 10 | +| member_type_enum | DEVELOPER | Platform core | keep | | H4a 2026-07-09 | RBAC vocabulary — keep all 10 | +| member_type_enum | MANAGER | Platform core | keep | | H4a 2026-07-09 | RBAC vocabulary — keep all 10 | +| member_type_enum | SUPPORT | Platform core | keep | | H4a 2026-07-09 | RBAC vocabulary — keep all 10 | +| member_type_enum | SERVICE | Platform core | keep | | H4a 2026-07-09 | RBAC vocabulary — keep all 10 | +| member_type_enum | SALES | Platform core | keep | | H4a 2026-07-09 | RBAC vocabulary — keep all 10 | +| member_type_enum | TECH | Platform core | keep | | H4a 2026-07-09 | RBAC vocabulary — keep all 10 | +| organization_type_enum | SOLAR_DEVELOPER | Platform core | keep | | | | +| organization_type_enum | LENDER | Platform core | keep | | | | +| organization_type_enum | DATA_AGGREGATOR | Platform core | keep | | | | +| organization_type_enum | PLATFORM_OPERATOR | Platform core | **add** | **#22** | H1c signed off 2026-07-09 | New value, not in legacy chain — ADR-007 Amendment (2026-07-09); enables DB-native admin-org RLS | +| external_system_enum | STEAMACO | Platform core | **drop** | **#28** | H4a 2026-07-09 | No live code refs | +| external_system_enum | CALIN | Platform core | keep | | H4a 2026-07-09 | Metering/device integration | +| external_system_enum | SOLCAST | Platform core | keep | | H4a 2026-07-09 | Production forecasting | +| external_system_enum | VICTRON | Platform core | keep | | H4a 2026-07-09 | Production monitoring | +| external_system_enum | FLUTTERWAVE | Platform core | keep | | H4a 2026-07-09 | Payments adapter | +| external_system_enum | AFRICASTALKING | Platform core | keep | | H4a 2026-07-09 | SMS/USSD notifications | +| external_system_enum | JOTFORM | Platform core | **drop** | **#28** | H4a 2026-07-09 | pd-hero dropped (#14); no code refs | +| external_system_enum | EPICOLLECT | Platform core | keep | | H4a 2026-07-09 | Field ops site pipeline | +| external_system_enum | JIRA | Platform core | keep | | H4a 2026-07-09 | `issues.external_tracking_system` default + Jira integration | +| external_system_enum | TELEGRAM | Platform core | keep | | H4a 2026-07-09 | Notification connector | +| external_system_enum | ZEROTIER | Platform core | keep | | H4a 2026-07-09 | Router monitoring | +| external_system_enum | MAKE | Platform core | keep | | H4a 2026-07-09 | Tier-3 integration (parameterized) | +| external_system_enum | FLOW_XO | Platform core | keep | | H4a 2026-07-09 | Tier-3 integration (parameterized) | +| external_system_enum | SENDGRID | Platform core | keep | | H4a 2026-07-09 | Email notifications | +| external_system_enum | ACREL | Platform core | **drop** | **#28** | H4a 2026-07-09 | No code refs | +| weather_type_enum | CLOUDY | Platform core | keep | | | | +| weather_type_enum | CLOUDS | Platform core | keep | | | | +| weather_type_enum | SHOWERS | Platform core | keep | | | | +| weather_type_enum | SUNNY | Platform core | keep | | | | +| weather_type_enum | UNKNOWN | Platform core | keep | | | | +| weather_type_enum | CLOUDY_WITH_RAIN | Platform core | keep | | | | +| communication_protocol_enum | CALIN_V1 | (2) Metering | keep | | H4b 2026-07-09 | CALIN API v1 path | +| communication_protocol_enum | CALIN_V2 | (2) Metering | keep | | H4b 2026-07-09 | CALIN API v2 path | +| communication_protocol_enum | CALIN_LORAWAN | (2) Metering | keep | | H4b 2026-07-09 | LoRaWAN path | +| fs_command_type_enum | ON | (2) Metering | keep | | H4b 2026-07-09 | | +| fs_command_type_enum | OFF | (2) Metering | keep | | H4b 2026-07-09 | | +| gender_enum | MALE | (2) Metering | keep | | H4b 2026-07-09 | | +| gender_enum | FEMALE | (2) Metering | keep | | H4b 2026-07-09 | | +| generator_type_enum | SMALL | (2) Metering | keep | | H4b 2026-07-09 | | +| generator_type_enum | LARGE | (2) Metering | keep | | H4b 2026-07-09 | | +| id_document_type_enum | PASSPORT | (2) Metering | keep | | H4b 2026-07-09 | | +| id_document_type_enum | NATIONAL_ID | (2) Metering | keep | | H4b 2026-07-09 | | +| id_document_type_enum | DRIVING_LICENSE | (2) Metering | keep | | H4b 2026-07-09 | | +| id_document_type_enum | VOTERS_CARD | (2) Metering | keep | | H4b 2026-07-09 | | +| meter_commissioning_status_enum | PENDING | (2) Metering | keep | | | | +| meter_commissioning_status_enum | PROCESSING | (2) Metering | keep | | | | +| meter_commissioning_status_enum | SUCCESSFUL | (2) Metering | keep | | | | +| meter_commissioning_status_enum | FAILED | (2) Metering | keep | | | | +| meter_interaction_status_enum | QUEUED | (2) Metering | keep | | | chain adds DEFERRED, SUSPENDED (20251111, 20260226) | +| meter_interaction_status_enum | ABORTED | (2) Metering | keep | | | chain adds DEFERRED, SUSPENDED (20251111, 20260226) | +| meter_interaction_status_enum | PROCESSING | (2) Metering | keep | | | chain adds DEFERRED, SUSPENDED (20251111, 20260226) | +| meter_interaction_status_enum | SUCCESSFUL | (2) Metering | keep | | | chain adds DEFERRED, SUSPENDED (20251111, 20260226) | +| meter_interaction_status_enum | FAILED | (2) Metering | keep | | | chain adds DEFERRED, SUSPENDED (20251111, 20260226) | +| meter_interaction_status_enum | DEFERRED | (2) Metering | keep | | | chain adds DEFERRED, SUSPENDED (20251111, 20260226) | +| meter_interaction_status_enum | SUSPENDED | (2) Metering | keep | | | chain adds DEFERRED, SUSPENDED (20251111, 20260226) | +| meter_interaction_type_enum | READ_CREDIT | (2) Metering | keep | | | chain grows to 19 values (20251110 → 20260428) | +| meter_interaction_type_enum | READ_POWER_LIMIT | (2) Metering | keep | | | chain grows to 19 values (20251110 → 20260428) | +| meter_interaction_type_enum | READ_VOLTAGE | (2) Metering | keep | | | chain grows to 19 values (20251110 → 20260428) | +| meter_interaction_type_enum | SET_POWER_LIMIT | (2) Metering | keep | | | chain grows to 19 values (20251110 → 20260428) | +| meter_interaction_type_enum | TOP_UP | (2) Metering | keep | | | chain grows to 19 values (20251110 → 20260428) | +| meter_interaction_type_enum | TURN_ON | (2) Metering | keep | | | chain grows to 19 values (20251110 → 20260428) | +| meter_interaction_type_enum | TURN_OFF | (2) Metering | keep | | | chain grows to 19 values (20251110 → 20260428) | +| meter_interaction_type_enum | READ_POWER | (2) Metering | keep | | | chain grows to 19 values (20251110 → 20260428) | +| meter_interaction_type_enum | READ_CURRENT | (2) Metering | keep | | | chain grows to 19 values (20251110 → 20260428) | +| meter_interaction_type_enum | CLEAR_CREDIT | (2) Metering | keep | | | chain grows to 19 values (20251110 → 20260428) | +| meter_interaction_type_enum | CLEAR_TAMPER | (2) Metering | keep | | | chain grows to 19 values (20251110 → 20260428) | +| meter_interaction_type_enum | READ_REPORT | (2) Metering | keep | | | chain grows to 19 values (20251110 → 20260428) | +| meter_interaction_type_enum | JOIN_NETWORK | (2) Metering | keep | | | chain grows to 19 values (20251110 → 20260428) | +| meter_interaction_type_enum | DELIVER_PREEXISTING_TOKEN | (2) Metering | keep | | | chain grows to 19 values (20251110 → 20260428) | +| meter_interaction_type_enum | READ_VERSION | (2) Metering | keep | | | chain grows to 19 values (20251110 → 20260428) | +| meter_interaction_type_enum | READ_DATE | (2) Metering | keep | | | chain grows to 19 values (20251110 → 20260428) | +| meter_interaction_type_enum | SET_DATE | (2) Metering | keep | | | chain grows to 19 values (20251110 → 20260428) | +| meter_interaction_type_enum | READ_TIME | (2) Metering | keep | | | chain grows to 19 values (20251110 → 20260428) | +| meter_interaction_type_enum | SET_TIME | (2) Metering | keep | | | chain grows to 19 values (20251110 → 20260428) | +| meter_phase_enum | SINGLE_PHASE | (2) Metering | keep | | | | +| meter_phase_enum | THREE_PHASE | (2) Metering | keep | | | | +| meter_type_enum | HPS | (2) Metering | keep | | | | +| meter_type_enum | FS | (2) Metering | keep | | | | +| mhi_operation_enum | ADD | (2) Metering | keep | | | | +| mhi_operation_enum | REMOVE | (2) Metering | keep | | | | +| mhi_status_enum | PENDING | (2) Metering | keep | | | | +| mhi_status_enum | PROCESSING | (2) Metering | keep | | | | +| mhi_status_enum | SUCCESSFUL | (2) Metering | keep | | | | +| mhi_status_enum | FAILED | (2) Metering | keep | | | | +| currency_enum | USD | (3) Payments | keep | | | | +| currency_enum | NGN | (3) Payments | keep | | | | +| currency_enum | EUR | (3) Payments | keep | | | | +| order_actor_type_enum | BANKING_SYSTEM | (3) Payments | keep | | | | +| order_actor_type_enum | ORGANIZATION | (3) Payments | keep | | | | +| order_actor_type_enum | CONNECTION | (3) Payments | keep | | | | +| order_actor_type_enum | METER | (3) Payments | keep | | | | +| order_actor_type_enum | AGENT | (3) Payments | keep | | | | +| order_actor_type_enum | CUSTOMER | (3) Payments | keep | | | | +| order_status_enum | INITIALISED | (3) Payments | keep | | | | +| order_status_enum | PENDING | (3) Payments | keep | | | | +| order_status_enum | COMPLETED | (3) Payments | keep | | | | +| order_status_enum | FAILED | (3) Payments | keep | | | | +| order_status_enum | CANCELLED | (3) Payments | keep | | | | +| order_status_enum | TIMED_OUT | (3) Payments | keep | | | | +| order_status_enum | IGNORED | (3) Payments | keep | | | | +| order_type_enum | ENERGY_TOPUP | (3) Payments | keep | | | | +| order_type_enum | CONNECTION_PAYMENT | (3) Payments | keep | | | | +| order_type_enum | CONNECTION_REFUND | (3) Payments | keep | | | | +| order_type_enum | AGENT_WITHDRAWAL | (3) Payments | keep | | | | +| order_type_enum | AGENT_TOPUP | (3) Payments | keep | | | | +| order_type_enum | ORGANIZATION_TOPUP | (3) Payments | keep | | | | +| order_type_enum | ORGANIZATION_WITHDRAWAL | (3) Payments | keep | | | | +| order_type_enum | CUSTOMER_TOPUP | (3) Payments | keep | | | | +| payment_channel_enum | USSD | (3) Payments | keep | | | | +| payment_channel_enum | AYRTON | (3) Payments | keep | | | | +| payment_channel_enum | NIFFLER | (3) Payments | keep | | | | +| payment_channel_enum | TELEGRAM | (3) Payments | keep | | | | +| payment_method_enum | CREDIT_CARD | (3) Payments | keep | | | | +| payment_method_enum | USSD | (3) Payments | keep | | | | +| payment_method_enum | BANK_TRANSFER | (3) Payments | keep | | | | +| transaction_status_enum | SUCCESSFUL | (3) Payments | keep | | | | +| transaction_status_enum | FAILED | (3) Payments | keep | | | | +| wallet_type_enum | VIRTUAL | (3) Payments | keep | | | | +| wallet_type_enum | REAL | (3) Payments | keep | | | | +| notification_status_enum | PENDING | (4) Notifications | keep | | | | +| notification_status_enum | PROCESSING | (4) Notifications | keep | | | | +| notification_status_enum | RECEIVED_BY_API | (4) Notifications | keep | | | | +| notification_status_enum | FAILED | (4) Notifications | keep | | | | +| notification_status_enum | SUCCESSFUL | (4) Notifications | keep | | | | +| notification_status_enum | READ | (4) Notifications | keep | | | | +| notification_status_enum | UNKNOWN | (4) Notifications | keep | | | | +| notification_type_enum | GRID_IS_HPS_ON_STATE_CHANGE | (4) Notifications | keep | | H4d 2026-07-09 | | +| notification_type_enum | GRID_IS_FS_ON_STATE_CHANGE | (4) Notifications | keep | | H4d 2026-07-09 | | +| notification_type_enum | GRID_METERING_HARDWARE_STATE_CHANGE | (4) Notifications | keep | | H4d 2026-07-09 | | +| notification_type_enum | FS_RULE_EXECUTION_COMING_UP | (4) Notifications | keep | | H4d 2026-07-09 | | +| notification_type_enum | FS_RULE_CHANGED | (4) Notifications | keep | | H4d 2026-07-09 | | +| notification_type_enum | TARIFF_RULE_CHANGED | (4) Notifications | keep | | H4d 2026-07-09 | | +| notification_type_enum | CLEAN_PANELS_REMINDER | (4) Notifications | keep | | H4d 2026-07-09 | | +| notification_type_enum | GRID_REVENUE | (4) Notifications | keep | | H4d 2026-07-09 | loch revenue-update | +| notification_type_enum | PASSWORD_RESET | (4) Notifications | keep | | H4d 2026-07-09 | sendgrid | +| notification_type_enum | INVITE | (4) Notifications | keep | | H4d 2026-07-09 | sendgrid | +| notification_type_enum | AUTO_PAYOUT_GENRATION_REPORT | (4) Notifications | **drop** | **#29** | H4d 2026-07-09 | payouts module dropped (#13) | +| notification_type_enum | CREDIT_SENT | (4) Notifications | keep | | H4d 2026-07-09 | | +| notification_type_enum | CREDIT_RECEIVED | (4) Notifications | keep | | H4d 2026-07-09 | tiamat USSD/orders | +| notification_type_enum | METER_TOPPED_UP | (4) Notifications | keep | | H4d 2026-07-09 | tiamat orders SMS | +| notification_type_enum | PAYMENT_REJECTED | (4) Notifications | keep | | H4d 2026-07-09 | tiamat orders | +| notification_type_enum | SITE_SUBMISSION | (4) Notifications | keep | | H4d 2026-07-09 | sendgrid, tiamat | +| issue_status_enum | OPEN | (5) Field ops | keep | | | | +| issue_status_enum | CLOSED | (5) Field ops | keep | | | | +| issue_status_enum | OVERRIDDEN | (5) Field ops | keep | | | | +| issue_type_enum | NO_COMMUNICATION | (5) Field ops | keep | | | | +| issue_type_enum | METER_NOT_ACTIVATED | (5) Field ops | keep | | | | +| issue_type_enum | TAMPER | (5) Field ops | keep | | | | +| issue_type_enum | POWER_LIMIT_BREACHED | (5) Field ops | keep | | | | +| issue_type_enum | OVER_VOLTAGE | (5) Field ops | keep | | | | +| issue_type_enum | LOW_VOLTAGE | (5) Field ops | keep | | | | +| issue_type_enum | POWER_LIMIT_BAD_CONFIGURATION | (5) Field ops | keep | | | | +| issue_type_enum | METER_STATE_BAD_CONFIGURATION | (5) Field ops | keep | | | | +| issue_type_enum | UNEXPECTED_POWER_LIMIT | (5) Field ops | keep | | | | +| issue_type_enum | UNEXPECTED_METER_STATUS | (5) Field ops | keep | | | | +| issue_type_enum | NO_CREDIT | (5) Field ops | keep | | | | +| issue_type_enum | NO_CONSUMPTION | (5) Field ops | keep | | | | +| issue_type_enum | NUMBER_OF_PHASES | (5) Field ops | keep | | | | +| issue_type_enum | VEBUS_STATE | (5) Field ops | keep | | | | +| issue_type_enum | VEBUS_ERROR | (5) Field ops | keep | | | | +| issue_type_enum | QUATTRO_TEMPERATURE_ALARM | (5) Field ops | keep | | | | +| issue_type_enum | QUATTRO_OVERLOAD_ALARM | (5) Field ops | keep | | | | +| issue_type_enum | HIGH_BATTERY_TEMPERATURE_ALARM | (5) Field ops | keep | | | | +| issue_type_enum | CELL_IMBALANCE_ALARM | (5) Field ops | keep | | | | +| issue_type_enum | HIGH_CHARGE_CURRENT_ALARM | (5) Field ops | keep | | | | +| issue_type_enum | HIGH_CHARGE_TEMPERATURE_ALARM | (5) Field ops | keep | | | | +| issue_type_enum | BATTERY_INTERNAL_FAILURE | (5) Field ops | keep | | | | +| issue_type_enum | BATTERY_CHARGE_BLOCKED_ALARM | (5) Field ops | keep | | | | +| issue_type_enum | BATTERY_DISCHARGE_BLOCKED_ALARM | (5) Field ops | keep | | | | +| mppt_type_enum | MPPT | (1) Production monitoring | keep | | | | +| mppt_type_enum | PV_INVERTER | (1) Production monitoring | keep | | | | +| solcast_cache_request_type_enum | ESTIMATED_ACTUALS | (1) Production monitoring | keep | | | | +| solcast_cache_request_type_enum | FORECAST | (1) Production monitoring | keep | | | | + +## Batch H5 — Triggers vs functions + +H5 confirms trigger wiring matches signed-off function decisions (H1–H3). **No new logic review** — H1b already reviewed the `append_rls_*` group together with their triggers; H5 is the formal trigger register pass before Task 5. + +**Out of scope (Task 3a, not in H5 register):** triggers on excluded/dropped objects — `directives`, `lorawan_directives`, `devices`/`device_logs`, `meter_credit_transfers`; Make.com grid webhooks (register #6, parameterized). + +**Known gap (app, not trigger):** `orders.rls_organization_id` has no INSERT trigger — set in tiamat `order-meta.ts` (documented in H1b). + +| sub-batch | scope | count | status | +|-----------|-------|------:|--------| +| **H5a** | `append_rls_*` INSERT triggers (legacy chain) | 19 | **signed off** 2026-07-09 | +| **H5b** | Auth triggers on `auth.users` | 2 | **signed off** 2026-07-09 | +| **H5c** | New admin-org GUC sync trigger (#22, not in legacy chain) | 1 | **signed off** 2026-07-09 | + +### H5a — `append_rls_*` INSERT triggers — signed off 2026-07-09 + +All are `BEFORE INSERT FOR EACH ROW`. **Keep all 19** — wiring matches H1b function register; bodies pick up #23 redesign at Task 5 without trigger DDL changes (except renames below). + +| # | trigger (legacy name) | ON table | function | action | register | +|---|------------------------|----------|----------|--------|----------| +| 1 | `append_rls_organization_id_on_member_insert` | `members` | `append_rls_organization_id_by_account_id()` | keep | — | +| 2 | `append_rls_organization_id_on_agent_insert` | `agents` | `append_rls_organization_id_by_grid_id()` | keep | — | +| 3 | `append_rls_organization_id_on_customer_insert` | `customers` | `append_rls_organization_id_by_grid_id()` | keep | — | +| 4 | `append_rls_organization_id_on_dcus_insert` | `dcus` | `append_rls_organization_id_by_grid_id()` | keep | — | +| 5 | `append_rls_organization_id_on_directive_batch_insert` | `directive_batches` | `append_rls_organization_id_by_grid_id()` | **rename** | **#16** → `…_on_meter_command_batch_insert` ON `meter_command_batches` | +| 6 | `append_rls_organization_id_on_energy_cabin_insert` | `energy_cabins` | `append_rls_organization_id_by_grid_id()` | keep | — | +| 7 | `append_rls_organization_id_on_mppt_insert` | `mppts` | `append_rls_organization_id_by_grid_id()` | keep | — | +| 8 | `append_rls_organization_id_on_pole_insert` | `poles` | `append_rls_organization_id_by_grid_id()` | keep | — | +| 9 | `append_rls_organization_id_on_route_insert` | `routers` | `append_rls_organization_id_by_grid_id()` | **rename** | **#10** → `…_on_router_insert` | +| 10 | `append_rls_organization_id_on_requested_connection_meters_inser` | `connection_requested_meters` | `append_rls_organization_id_by_connection_id()` | keep | legacy name truncated at 63 chars | +| 11 | `append_rls_organization_id_on_connection_insert` | `connections` | `append_rls_organization_id_by_customer_id()` | keep | — | +| 12 | `append_rls_organization_id_on_meter_insert` | `meters` | `append_rls_organization_id_by_connection_id()` | keep | — | +| 13 | `append_rls_organization_id_on_note_insert` | `notes` | `append_rls_organization_id_by_customer_id()` | keep | — | +| 14 | `append_rls_organization_id_on_wallet_insert` | `wallets` | `append_rls_organization_id_by_customer_id_or_agent_id_or_connec()` | keep | — | +| 15 | `append_rls_organization_id_on_meter_install_session_insert` | `metering_hardware_install_sessions` | `append_rls_organization_id_by_dcu_id_or_meter_id()` | keep | — | +| 16 | `append_rls_organization_id_on_meter_commissioning_insert` | `meter_commissionings` | `append_rls_organization_id_by_metering_hardware_install_session()` | keep | — | +| 17 | `append_rls_organization_id_on_directive_batch_execution_insert` | `directive_batch_executions` | `append_rls_organization_id_by_directive_batch_id()`¹ | **rename** | **#16** → `…_on_meter_command_batch_execution_insert` ON `meter_command_batch_executions`; function → `by_meter_command_batch_id()` | +| 18 | `append_rls_organization_id_on_issue_insert` | `issues` | `append_rls_organization_id_by_meter_id()` | keep | — | +| 19 | `append_rls_organization_id_on_transaction_insert` | `transactions` | `append_rls_organization_id_by_order_id()` | keep | — | + +¹ Function rename is register #16; trigger DDL unchanged except name + target table. + +**Cross-check:** 27 public triggers in legacy chain − 5 on excluded/dropped tables − 3 Make.com (#6, parameterized) = **19 keep** ✓ + +### H5b — Auth triggers — signed off 2026-07-09 + +| trigger | ON table | function | timing | decision | +|---------|----------|----------|--------|----------| +| `on_auth_user_created` | `auth.users` | `handle_new_user()` | AFTER INSERT | **keep** | +| `on_auth_user_updated` | `auth.users` | `handle_update_user()` | AFTER UPDATE | **keep** | + +### H5c — Admin org GUC sync trigger — signed off 2026-07-09 + +**Add** (register **#22**, not in legacy chain): + +| trigger | ON table | function | timing | register | +|---------|----------|----------|--------|----------| +| `sync_admin_organization_id_guc` | `organizations` | `sync_admin_organization_id_guc()` | AFTER INSERT OR UPDATE OF `organization_type` OR DELETE | **#22** | + +Maintains `app.admin_organization_id` GUC for `rls_check_if_admin_org_member()`. Full spec: ADR-007 Amendment + Programmability adjustments §3. + +## Register — functions (all batches) + +| function | batch | action | register § | rationale | notes | +|----------|-------|--------|------------|-----------|-------| +| `rls_check_if_nxt_member()` | H1c | **rename + redesign** | **#22** | H1c 2026-07-09 | → `rls_check_if_admin_org_member()`; GUC-backed; `STABLE` | +| `rls_check_if_lender()` | H1 | keep | | H1a 2026-07-09 | JWT organization_type = LENDER | +| `rls_get_member_org_id()` | H1 | keep | | provisional | JWT organization_id | +| `append_rls_organization_id_by_account_id()` | H1b | keep + redesign | #23 | H1b 2026-07-09 | dead join to `organizations` removed; triggers: `append_rls_organization_id_on_member_insert` ON `members` | +| `append_rls_organization_id_by_grid_id()` | H1b | keep + redesign | #23 | H1b 2026-07-09 | delegates to `rls_org_id_from_grid()`; triggers: `append_rls_organization_id_on_agent_insert` ON `agents`, `append_rls_organization_id_on_customer_insert` ON `customers`, `append_rls_organization_id_on_dcus_insert` ON `dcus`, `append_rls_organization_id_on_directive_batch_insert` ON `directive_batches`, `append_rls_organization_id_on_energy_cabin_insert` ON `energy_cabins`, `append_rls_organization_id_on_mppt_insert` ON `mppts`, `append_rls_organization_id_on_pole_insert` ON `poles`, `append_rls_organization_id_on_route_insert` ON `routers` | +| `append_rls_organization_id_by_historical_grid_id()` | H1a | **drop** | **#21** | H1a 2026-07-09 | Orphan — no trigger | +| `append_rls_organization_id_by_connection_id()` | H1b | keep + redesign | #23 | H1b 2026-07-09 | delegates to `rls_org_id_from_connection()`; triggers: `append_rls_organization_id_on_requested_connection_meters_inser` ON `connection_requested_meters`, `append_rls_organization_id_on_meter_insert` ON `meters` | +| `append_rls_organization_id_by_customer_id()` | H1b | keep + redesign | #23 | H1b 2026-07-09 | delegates to `rls_org_id_from_customer()`; triggers: `append_rls_organization_id_on_connection_insert` ON `connections`, `append_rls_organization_id_on_note_insert` ON `notes` | +| `append_rls_organization_id_by_customer_id_or_agent_id_or_connec()` | H1b | keep + redesign | #23 | H1b 2026-07-09 | branches delegate to helpers; 2nd dead lookup removed; triggers: `append_rls_organization_id_on_wallet_insert` ON `wallets` | +| `append_rls_organization_id_by_dcu_id_or_meter_id()` | H1b | keep + redesign | #23 | H1b 2026-07-09 | branches delegate to `rls_org_id_from_dcu()` / `rls_org_id_from_meter()`; triggers: `append_rls_organization_id_on_meter_install_session_insert` | +| `append_rls_organization_id_by_directive_batch_id()` | H1a + H1b | rename + redesign | #16, #23 | H1a + H1b 2026-07-09 | → `append_rls_organization_id_by_meter_command_batch_id()`; delegates to `rls_org_id_from_grid()` | +| `append_rls_organization_id_by_meter_id()` | H1b | keep + redesign | #23 | H1b 2026-07-09 | delegates to `rls_org_id_from_meter()`; triggers: `append_rls_organization_id_on_issue_insert` ON `issues` | +| `append_rls_organization_id_by_metering_hardware_install_session()` | H1b | keep + redesign | #23 | H1b 2026-07-09 | delegates to `rls_org_id_from_meter()` via session's meter_id; triggers: `append_rls_organization_id_on_meter_commissioning_insert` | +| `append_rls_organization_id_by_order_id()` | H1b | keep + redesign | #23 | H1b 2026-07-09 | delegates to `rls_org_id_from_grid()` via historical_grid_id; triggers: `append_rls_organization_id_on_transaction_insert` ON `transactions` | +| `rls_org_id_from_grid(grid_id)` | H1b | **add** | #23 | H1b 2026-07-09 | new leaf helper; `LANGUAGE sql STABLE SECURITY DEFINER` | +| `rls_org_id_from_customer(customer_id)` | H1b | **add** | #23 | H1b 2026-07-09 | new helper; delegates to `rls_org_id_from_grid()` | +| `rls_org_id_from_connection(connection_id)` | H1b | **add** | #23 | H1b 2026-07-09 | new helper; delegates to `rls_org_id_from_customer()` | +| `rls_org_id_from_agent(agent_id)` | H1b | **add** | #23 | H1b 2026-07-09 | new helper; delegates to `rls_org_id_from_grid()` | +| `rls_org_id_from_meter(meter_id)` | H1b | **add** | #23 | H1b 2026-07-09 | new helper; delegates to `rls_org_id_from_connection()` | +| `rls_org_id_from_dcu(dcu_id)` | H1b | **add** | #23 | H1b 2026-07-09 | new helper; delegates to `rls_org_id_from_grid()` | +| `handle_new_user()` | H2 | keep | | H2 2026-07-09 | auth.users AFTER INSERT — creates account row; triggers: `on_auth_user_created` ON `auth.users` | +| `handle_update_user()` | H2 | keep | | H2 2026-07-09 | auth.users AFTER UPDATE — syncs account.organization_id from auth app_metadata; triggers: `on_auth_user_updated` ON `auth.users` | +| `get_grid_status(grid_id integer)` | H2 | keep + redesign | **#26** | H2 2026-07-09 | RPC — pegasus grid dashboard; drop `are_all_dcus_online` + `are_all_dcus_under_high_load_threshold` from return type (register #17); mark `STABLE` | +| `find_energy_topup_revenue(grid_id, start_date, end_date)` | H3 | keep + STABLE | **#27** | H3 2026-07-09 | RPC — revenue aggregate on orders.historical_grid_id; mark `STABLE` | +| `find_top_spenders(grid_id, limit_count, start_date, end_date)` | H3 | keep + STABLE + fix | **#27** | H3 2026-07-09 | RPC — top spenders; mark `STABLE`; GROUP BY customer fields only (drop meter id from group) | +| `lock_next_order_and_wallets(uuid)` | H3 | keep + harden | **#24** | H3 2026-07-09 | RPC — pessimistic lock (20251231155410); `SET search_path TO ''` (#24); logic confirmed keep | + +## Register — triggers (H5) + +| trigger | on table | function | action | register § | rationale | notes | +|---------|----------|----------|--------|------------|-----------|-------| +| append_rls_organization_id_on_agent_insert | agents | append_rls_organization_id_by_grid_id() | keep | | H5a 2026-07-09 | | +| append_rls_organization_id_on_requested_connection_meters_inser | connection_requested_meters | append_rls_organization_id_by_connection_id() | keep | | H5a 2026-07-09 | truncated legacy name | +| append_rls_organization_id_on_connection_insert | connections | append_rls_organization_id_by_customer_id() | keep | | H5a 2026-07-09 | | +| append_rls_organization_id_on_customer_insert | customers | append_rls_organization_id_by_grid_id() | keep | | H5a 2026-07-09 | | +| append_rls_organization_id_on_dcus_insert | dcus | append_rls_organization_id_by_grid_id() | keep | | H5a 2026-07-09 | | +| append_rls_organization_id_on_directive_batch_execution_insert | directive_batch_executions | append_rls_organization_id_by_directive_batch_id() | rename | #16 | H5a 2026-07-09 | → `…_on_meter_command_batch_execution_insert` ON `meter_command_batch_executions` | +| append_rls_organization_id_on_directive_batch_insert | directive_batches | append_rls_organization_id_by_grid_id() | rename | #16 | H5a 2026-07-09 | → `…_on_meter_command_batch_insert` ON `meter_command_batches` | +| append_rls_organization_id_on_energy_cabin_insert | energy_cabins | append_rls_organization_id_by_grid_id() | keep | | H5a 2026-07-09 | | +| append_rls_organization_id_on_issue_insert | issues | append_rls_organization_id_by_meter_id() | keep | | H5a 2026-07-09 | | +| append_rls_organization_id_on_member_insert | members | append_rls_organization_id_by_account_id() | keep | | H5a 2026-07-09 | | +| append_rls_organization_id_on_meter_commissioning_insert | meter_commissionings | append_rls_organization_id_by_metering_hardware_install_session() | keep | | H5a 2026-07-09 | | +| append_rls_organization_id_on_meter_install_session_insert | metering_hardware_install_sessions | append_rls_organization_id_by_dcu_id_or_meter_id() | keep | | H5a 2026-07-09 | | +| append_rls_organization_id_on_meter_insert | meters | append_rls_organization_id_by_connection_id() | keep | | H5a 2026-07-09 | | +| append_rls_organization_id_on_mppt_insert | mppts | append_rls_organization_id_by_grid_id() | keep | | H5a 2026-07-09 | | +| append_rls_organization_id_on_note_insert | notes | append_rls_organization_id_by_customer_id() | keep | | H5a 2026-07-09 | | +| append_rls_organization_id_on_pole_insert | poles | append_rls_organization_id_by_grid_id() | keep | | H5a 2026-07-09 | | +| append_rls_organization_id_on_route_insert | routers | append_rls_organization_id_by_grid_id() | rename | #10 | H5a 2026-07-09 | → `…_on_router_insert` | +| append_rls_organization_id_on_transaction_insert | transactions | append_rls_organization_id_by_order_id() | keep | | H5a 2026-07-09 | | +| append_rls_organization_id_on_wallet_insert | wallets | append_rls_organization_id_by_customer_id_or_agent_id_or_connec() | keep | | H5a 2026-07-09 | | +| on_auth_user_created | auth.users | handle_new_user() | keep | | H5b 2026-07-09 | H2 signed off | +| on_auth_user_updated | auth.users | handle_update_user() | keep | | H5b 2026-07-09 | H2 signed off | +| sync_admin_organization_id_guc | organizations | sync_admin_organization_id_guc()¹ | **add** | **#22** | H5c 2026-07-09 | Not in legacy chain; maintains GUC `app.admin_organization_id` | + +¹ Proposed names for Task 5 — trigger + handler function (see register #22, Programmability §3). + +**Workflow:** + +1. Maintainer reviews batches H1–H5; edit actions in the tables above. +2. On sign-off per batch: sync confirmed drops/renames/enum value drops to `002b-schema-deviation-register.md` **Programmability adjustments**. +3. Task 3 complete when every row has a final action and register is synced. + +**Out of scope:** dropped/excluded/parameterize objects (Task 3a); table columns (Task 3b complete). + +**H1 (RLS helpers) fully closed 2026-07-09** — H1a, H1b, H1c all signed off. + +**H2 (auth + platform RPCs) fully closed 2026-07-09** — `handle_new_user()` keep; `handle_update_user()` keep; `get_grid_status()` keep + redesign (#26). + +**H3 (payments RPCs) fully closed 2026-07-09** — `lock_next_order_and_wallets()` keep (#24 search_path); `find_energy_topup_revenue()` keep + `STABLE` (#27); `find_top_spenders()` keep + `STABLE` + GROUP BY fix (#27). + +**H4 (enum value trim) fully closed 2026-07-09** — H4a: `external_system_enum` drop 3 (#28); H4b/H4c/H4e keep all; H4d drop `AUTO_PAYOUT_GENRATION_REPORT` (#29). + +**H5 fully closed 2026-07-09** — H5a: keep 19 + renames #10/#16; H5b: keep 2 auth; H5c: add `sync_admin_organization_id_guc` (#22). + +**Task 3c complete.** Next: **Task 3d** (database-wide performance audit). diff --git a/docs/plans/002-oss-migration/002b-task5-generation/README.md b/docs/plans/002-oss-migration/002b-task5-generation/README.md new file mode 100644 index 0000000..50af4b7 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/README.md @@ -0,0 +1,99 @@ +# Task 5 generation pipeline + +This folder is the working pipeline used to author +`supabase/migrations/20260710120000_init.sql` — the canonical OSS baseline +init migration — from the legacy database's schema. It is **not** run as +part of any build, CI, or app code path; it exists purely so Task 5 doesn't +have to be redone by hand if it ever needs to be re-run (e.g. a Task 6 A/B +diff turns up a discrepancy that's cheaper to fix by regenerating than by +hand-editing 3,000+ lines of SQL, or the legacy schema changes before +cutover). + +**Source of truth for *why*:** every keep/drop/rename/modify decision this +pipeline encodes comes from +`docs/plans/002-oss-migration/002b-schema-deviation-register.md`. This +pipeline is only the *how* — it turns those decisions into SQL text +mechanically, so the final migration is provably consistent with the +register instead of hand-transcribed. + +## How it works + +1. `schema-reference.sql` — a `pg_dump --schema-only` of the legacy + reference database (owned schemas). This is the one true input; every + other file here is derived from it. +2. `parse_dump.py` — parses the dump into structured blocks (keyed by + `pg_dump`'s own `-- Name: X; Type: Y; Schema: Z; Owner: W` comment + headers) and writes `blocks.json` (gitignored-equivalent — not checked + in, since it's fully regenerable from `schema-reference.sql` in under a + second) and `sections/preamble.sql`. +3. `registry.py` — the single source of truth for keep/drop/rename/modify + decisions, transcribed from the deviation register. Every `gen_*.py` + script imports from here; there is no other place transformation logic + lives. +4. `gen_tables.py`, `gen_sequences.py`, `gen_constraints.py`, + `gen_indexes.py`, `gen_views.py`, `gen_triggers.py`, `gen_policies.py`, + `gen_grants.py`, `gen_rls_enable.py` — each reads `blocks.json` + + `registry.py` and writes one section's SQL body to `sections/`. Some + sections (extensions, enum types, functions, and the 48 new + register-#25/#30 indexes) were hand-authored rather than mechanically + derived — see `sections/01-extensions.sql`, `02-types.sql`, + `10-functions-body.sql`, `08b-new-indexes.sql` directly. +5. `verify_types.py` — sanity-checks the hand-authored enum types section + against `registry.py`'s decisions. +6. `assemble.py` — concatenates every section (hand-authored + generated) in + dependency order (extensions → types → tables → sequences → views → + constraints → indexes → functions → triggers → RLS/policies → grants) + with section-header comments, and writes the final migration file. + +## Re-running the full pipeline + +From this directory: + +```bash +python3 parse_dump.py +python3 gen_tables.py +python3 gen_sequences.py +python3 gen_constraints.py +python3 gen_indexes.py +python3 gen_views.py +python3 gen_triggers.py +python3 gen_policies.py +python3 gen_grants.py +python3 gen_rls_enable.py +python3 verify_types.py # sanity check only, no output file +python3 assemble.py +``` + +`assemble.py` writes to `supabase/migrations/20260710120000_init.sql` +(computed relative to the repo root, so this works regardless of cwd). +Verified byte-for-byte reproducible against the original 2026-07-10 +migration as of the last time this pipeline was moved/refactored. + +**If you're regenerating because the decisions changed** (not just +verifying reproducibility): update `registry.py` and/or the relevant +hand-authored `sections/*.sql` file first, keep the deviation register in +sync, then re-run. If the result is a genuinely new migration (not a +same-day fix to the unapplied original), give it a fresh timestamp filename +in `assemble.py` — Supabase requires migration filenames to sort after any +already-applied migration. + +## Regenerating `schema-reference.sql` itself + +If the legacy reference database's schema has changed and you need a fresh +dump: + +```bash +cd legacy && npx supabase@2.109.1 start +pg_dump --schema-only --schema=public \ + "postgres://postgres:postgres@127.0.0.1:54322/postgres" \ + > ../docs/plans/002-oss-migration/002b-task5-generation/schema-reference.sql +npx supabase@2.109.1 stop +``` + +(Adjust the connection string/port to whatever `supabase start` reports for +your local stack. The dump is `public`-schema-only — the `auth` schema is +never dumped. The 2 `auth.users` triggers are hand-appended directly in +`gen_triggers.py`; their trigger functions (`handle_new_user`, +`handle_update_user`) are hand-authored in `sections/10-functions-body.sql`. +Both are carried over unchanged from the legacy migration chain, not +derived from this dump.) diff --git a/docs/plans/002-oss-migration/002b-task5-generation/assemble.py b/docs/plans/002-oss-migration/002b-task5-generation/assemble.py new file mode 100644 index 0000000..e31580b --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/assemble.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +"""Assemble the final init migration from the authored section files, in +dependency order (per the Task 5 plan: extensions -> types -> tables -> +constraints -> functions -> triggers -> policies -> grants, with sequences/ +views/indexes interspersed where FK/dependency order requires). + +Re-running this writes over the original 2026-07-10 init migration file. If +re-generating for a *new* migration (not just reproducing/verifying the +original), change OUT_PATH's filename to a fresh timestamp first — Supabase +requires migration filenames to sort after any already-applied migration. +""" +from pathlib import Path + +BASE = Path(__file__).parent +REPO_ROOT = BASE.parents[3] + +def read(path): + with open(BASE / "sections" / path) as f: + return f.read().rstrip("\n") + +def section(title, body_text, note=None): + lines = [ + "-- =============================================================================", + f"-- {title}", + "-- =============================================================================", + ] + if note: + lines.append(note) + lines.append("") + lines.append(body_text) + return "\n".join(lines) + +parts = [] + +# 1. Extensions (already carries the whole-file preamble + its own header) +parts.append(read("01-extensions.sql")) + +# 2. Enum types (already has its own header) +parts.append(read("02-types.sql")) + +# 3. Tables +parts.append(section( + "Tables", + read("04-tables-body.sql"), + "-- 35 of the legacy chain's 57 tables are kept, carried over verbatim except\n" + "-- for the column drops/renames and table renames recorded in the deviation\n" + "-- register's \"Column adjustments\" section and register #16.", +)) + +# 4. Sequences +parts.append(section( + "Sequences", + read("05-sequences-body.sql"), + "-- One sequence (or identity-column sequence) per kept table's primary key.\n" + "-- 3 tables (meter_interactions, pd_sites, pd_site_submissions) use identity\n" + "-- columns rather than a standalone sequence + DEFAULT nextval(); pg_dump\n" + "-- renders both forms, kept as dumped.", +)) + +# 5. Views +parts.append(section( + "Views", + read("09-views-body.sql"), + "-- 3 of the legacy chain's 4 views are kept (agents_with_account,\n" + "-- customers_with_account, meters_with_account_and_statuses), with the same\n" + "-- column drops applied as their underlying tables. batch_commands is dropped\n" + "-- (register #9 — read path for the dropped directive-batch tables).", +)) + +# 6. Constraints (PK/UNIQUE, then FK) +constraints_body = read("06-constraints-body.sql") + "\n\n" + read("07-fk-constraints-body.sql") +parts.append(section( + "Constraints", + constraints_body, + "-- Primary key and unique constraints first, then foreign keys.\n" + "-- FK cycle hardening (register #34): 5 denormalized \"latest pointer\" FKs get\n" + "-- ON DELETE SET NULL so a referenced row's deletion doesn't require deleting\n" + "-- the pointer's owner; their 5 structural back-pointer counterparts are left\n" + "-- at the implicit NO ACTION default (deleting the pointed-to row must fail,\n" + "-- or cascade through the owning aggregate root, unless the pointer is\n" + "-- cleared first).", +)) + +# 7. Indexes +indexes_body = read("08-indexes-body.sql") + "\n\n" + read("08b-new-indexes.sql") +parts.append(section( + "Indexes", + indexes_body, + "-- Existing kept indexes (renamed where their table was renamed), followed by\n" + "-- 49 new indexes: register #22 (partial unique on organizations), plus 48\n" + "-- FK/RLS-predicate coverage gaps from the schema audit (register #25, #30).", +)) + +# 8. Functions (already has its own header) +parts.append(read("10-functions-body.sql")) + +# 9. Triggers +parts.append(section( + "Triggers", + read("11-triggers-body.sql"), + "-- 21 triggers: 19 append_rls_organization_id_* triggers backing denormalized\n" + "-- RLS columns (3 renamed alongside their table, register #16) and the 2\n" + "-- auth.users triggers carried over from the legacy chain unchanged.", +)) + +# 10. Row-level security: enable, then policies +rls_body = read("14-rls-enable-body.sql") + "\n\n" + read("13-policies-body.sql") +parts.append(section( + "Row-level security", + rls_body, + "-- RLS is enabled on all 35 kept tables (unchanged from the legacy chain).\n" + "-- 76 of the legacy chain's 123 policies are kept — 26 dropped with their\n" + "-- table, 21 dropped for targeting company-infra readonly roles\n" + "-- (grafana_readonly, make_readonly, snaplet_readonly_2) that this baseline\n" + "-- does not provision. 18 policies are rewritten to wrap their RLS helper\n" + "-- call in `( SELECT … )` for per-statement (not per-row) evaluation (register\n" + "-- #32) — the other 51 helper-calling policies were already wrapped upstream.", +)) + +# 11. Grants +parts.append(section( + "Grants (Data API access)", + read("12-grants-body.sql"), + "-- Explicit per-object GRANTs for every kept table, sequence, and function, to\n" + "-- anon/authenticated/service_role (register #33) — required for Data API\n" + "-- (PostgREST/GraphQL) reachability; Supabase stopped auto-granting these on\n" + "-- new projects from 2026-05-30. The corresponding 3 ALTER DEFAULT PRIVILEGES\n" + "-- statements are deliberately omitted: future tables get explicit grants per\n" + "-- migration instead of an ambient auto-expose default.", +)) + +final = "\n\n".join(parts) + "\n" + +OUT_PATH = REPO_ROOT / "supabase" / "migrations" / "20260710120000_init.sql" +OUT_PATH.parent.mkdir(parents=True, exist_ok=True) +with open(OUT_PATH, "w") as f: + f.write(final) + +print("Written:", OUT_PATH) +print("Total lines:", final.count("\n")) diff --git a/docs/plans/002-oss-migration/002b-task5-generation/gen_constraints.py b/docs/plans/002-oss-migration/002b-task5-generation/gen_constraints.py new file mode 100644 index 0000000..9bac89d --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/gen_constraints.py @@ -0,0 +1,118 @@ +import json, re +from registry import DROP_TABLES, RENAME_TABLES, COLUMN_CHANGES, rename_table +from pathlib import Path + +BASE = Path(__file__).parent + +blocks = json.load(open(BASE / "blocks.json")) + +FK_HARDEN = { + # (constrained_table_after_rename, constrained_column): referenced_table_after_rename + ("meters", "last_metering_hardware_install_session_id"): "metering_hardware_install_sessions", + ("metering_hardware_install_sessions", "last_metering_hardware_import_id"): "metering_hardware_imports", + ("metering_hardware_install_sessions", "last_meter_commissioning_id"): "meter_commissionings", + ("dcus", "last_metering_hardware_install_session_id"): "metering_hardware_install_sessions", + ("meters", "last_encountered_issue_id"): "issues", +} + +def table_of(block_name): + return block_name.split(" ", 1)[0] + +def dropped_cols_for(table_after_rename): + return set(COLUMN_CHANGES.get(table_after_rename, {}).get("drop", [])) + +def rename_cols_for(table_after_rename): + return COLUMN_CHANGES.get(table_after_rename, {}).get("rename", {}) + +def apply_table_renames(text): + for old, new in RENAME_TABLES.items(): + text = re.sub(rf"\bpublic\.{old}\b", f"public.{new}", text) + return text + +def apply_constraint_name_renames(text): + # Constraint names are bare identifiers auto-generated from the original + # table name (no "public." prefix), so apply_table_renames' regex misses + # them; rename the ADD CONSTRAINT identifier itself where it still + # starts with an old (pre-rename) table name. + for old, new in RENAME_TABLES.items(): + text = re.sub(rf"(ADD CONSTRAINT (?:\"|)){old}_", rf"\1{new}_", text) + return text + +def apply_col_rename_in_paren(text, table_after_rename): + rename_map = rename_cols_for(table_after_rename) + if not rename_map: + return text + def repl(m): + cols = [c.strip() for c in m.group(1).split(",")] + cols = [rename_map.get(c, c) for c in cols] + return "(" + ", ".join(cols) + ")" + # only rewrite the column-list parens right after FOREIGN KEY / PRIMARY KEY / UNIQUE + text = re.sub(r"(FOREIGN KEY|PRIMARY KEY|UNIQUE) \(([^)]+)\)", lambda m: f"{m.group(1)} (" + ", ".join(rename_map.get(c.strip(), c.strip()) for c in m.group(2).split(",")) + ")", text) + return text + +# ---------- PK/UNIQUE constraints ---------- +constraint_blocks = [b for b in blocks if b["type"] == "CONSTRAINT"] +kept_constraints = [] +for b in constraint_blocks: + orig_table = table_of(b["name"]) + if orig_table in DROP_TABLES: + continue + new_table = rename_table(orig_table) + dropped = dropped_cols_for(new_table) + m = re.search(r"(PRIMARY KEY|UNIQUE) \(([^)]+)\)", b["body"]) + cols = [c.strip() for c in m.group(2).split(",")] if m else [] + if any(c in dropped for c in cols): + continue + body = apply_table_renames(b["body"]) + body = apply_constraint_name_renames(body) + body = apply_col_rename_in_paren(body, new_table) + kept_constraints.append(body.strip()) + +with open(BASE / "sections" / "06-constraints-body.sql", "w") as f: + f.write("\n\n".join(kept_constraints) + "\n") +print("Kept PK/UNIQUE constraints:", len(kept_constraints), "of", len(constraint_blocks)) + +# ---------- FK constraints ---------- +fk_blocks = [b for b in blocks if b["type"] == "FK CONSTRAINT"] +kept_fks = [] +dropped_for_table_reason = 0 +dropped_for_column_reason = 0 +for b in fk_blocks: + orig_table = table_of(b["name"]) + if orig_table in DROP_TABLES: + dropped_for_table_reason += 1 + continue + new_table = rename_table(orig_table) + m = re.search(r"FOREIGN KEY \(([^)]+)\) REFERENCES public\.(\w+)\(", b["body"]) + if m is None: + # FK to a non-public schema (e.g. accounts.supabase_id -> auth.users) — always kept + cols = [] + ref_table_orig = None + else: + cols = [c.strip() for c in m.group(1).split(",")] + ref_table_orig = m.group(2) + if ref_table_orig is not None and ref_table_orig in DROP_TABLES: + dropped_for_table_reason += 1 + continue + dropped = dropped_cols_for(new_table) + if any(c in dropped for c in cols): + dropped_for_column_reason += 1 + continue + body = apply_table_renames(b["body"]) + body = apply_constraint_name_renames(body) + body = apply_col_rename_in_paren(body, new_table) + # FK hardening: add ON DELETE SET NULL for the 5 forward pointers + ref_table_new = rename_table(ref_table_orig) if ref_table_orig is not None else None + rename_map = rename_cols_for(new_table) + renamed_cols = [rename_map.get(c, c) for c in cols] + if len(renamed_cols) == 1 and (new_table, renamed_cols[0]) in FK_HARDEN and FK_HARDEN[(new_table, renamed_cols[0])] == ref_table_new: + body = body.rstrip() + assert body.endswith(");") + body = body[:-1] + " ON DELETE SET NULL;" + kept_fks.append(body.strip()) + +with open(BASE / "sections" / "07-fk-constraints-body.sql", "w") as f: + f.write("\n\n".join(kept_fks) + "\n") +print("Kept FK constraints:", len(kept_fks), "of", len(fk_blocks)) +print(" dropped (table reason):", dropped_for_table_reason) +print(" dropped (column reason):", dropped_for_column_reason) diff --git a/docs/plans/002-oss-migration/002b-task5-generation/gen_grants.py b/docs/plans/002-oss-migration/002b-task5-generation/gen_grants.py new file mode 100644 index 0000000..9afd456 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/gen_grants.py @@ -0,0 +1,91 @@ +import json, re +from pathlib import Path +from registry import ( + DROP_TABLES, RENAME_TABLES, DROP_SEQUENCES, RENAME_SEQUENCES, + DROP_FUNCTIONS, RENAME_FUNCTIONS, PARAMETERIZE_ROLES, rename_table, + DROP_VIEWS, +) + +BASE = Path(__file__).parent + +blocks = json.load(open(BASE / "blocks.json")) +acl_blocks = [b for b in blocks if b["type"] == "ACL"] + +def filter_readonly_lines(body): + lines = [l for l in body.split("\n") if l.strip()] + kept = [l for l in lines if not any(f"TO {role};" in l for role in PARAMETERIZE_ROLES)] + return kept + +def apply_table_renames(text): + for old, new in RENAME_TABLES.items(): + text = re.sub(rf"\bpublic\.{old}\b", f"public.{new}", text) + return text + +def apply_seq_renames(text): + for old, new in RENAME_SEQUENCES.items(): + text = re.sub(rf"\bpublic\.{old}\b", f"public.{new}", text) + return text + +def apply_func_renames(text): + for old, new in RENAME_FUNCTIONS.items(): + text = re.sub(rf"\bpublic\.{old}\(\)", f"public.{new}()", text) + return text + +out = [] + +# --- SCHEMA --- +for b in acl_blocks: + if b["name"] == "SCHEMA public": + out.append("\n".join(filter_readonly_lines(b["body"]))) + +# --- TABLE --- +for b in acl_blocks: + if not b["name"].startswith("TABLE "): + continue + orig_table = b["name"][len("TABLE "):] + if orig_table in DROP_TABLES or orig_table in DROP_VIEWS: + continue + lines = filter_readonly_lines(b["body"]) + text = apply_table_renames("\n".join(lines)) + out.append(text) + +# --- SEQUENCE --- +for b in acl_blocks: + if not b["name"].startswith("SEQUENCE "): + continue + orig_seq = b["name"][len("SEQUENCE "):] + if orig_seq in DROP_SEQUENCES: + continue + lines = filter_readonly_lines(b["body"]) + text = apply_table_renames(apply_seq_renames("\n".join(lines))) + out.append(text) + +# --- FUNCTION --- +for b in acl_blocks: + if not b["name"].startswith("FUNCTION "): + continue + m = re.match(r"FUNCTION (\w+)\(", b["name"]) + orig_fn = m.group(1) + if orig_fn in DROP_FUNCTIONS: + continue + lines = filter_readonly_lines(b["body"]) + text = apply_func_renames("\n".join(lines)) + out.append(text) + +# --- New functions not present in the legacy dump (register #23, #22) --- +NEW_FUNCTIONS = [ + "rls_org_id_from_grid(integer)", + "rls_org_id_from_customer(integer)", + "rls_org_id_from_connection(integer)", + "rls_org_id_from_agent(integer)", + "rls_org_id_from_meter(integer)", + "rls_org_id_from_dcu(integer)", +] +for fn in NEW_FUNCTIONS: + lines = [f'GRANT ALL ON FUNCTION public.{fn} TO {role};' for role in ("anon", "authenticated", "service_role")] + out.append("\n".join(lines)) + +with open(BASE / "sections" / "12-grants-body.sql", "w") as f: + f.write("\n\n".join(out) + "\n") + +print("Grant blocks emitted:", len(out)) diff --git a/docs/plans/002-oss-migration/002b-task5-generation/gen_indexes.py b/docs/plans/002-oss-migration/002b-task5-generation/gen_indexes.py new file mode 100644 index 0000000..415b3fb --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/gen_indexes.py @@ -0,0 +1,48 @@ +import json, re +from registry import DROP_TABLES, RENAME_TABLES, COLUMN_CHANGES, rename_table +from pathlib import Path + +BASE = Path(__file__).parent + +blocks = json.load(open(BASE / "blocks.json")) +index_blocks = [b for b in blocks if b["type"] == "INDEX"] + +def apply_table_renames(text): + for old, new in RENAME_TABLES.items(): + text = re.sub(rf"\bpublic\.{old}\b", f"public.{new}", text) + return text + +kept = [] +dropped_table_reason = 0 +dropped_col_reason = 0 +for b in index_blocks: + m = re.search(r"ON public\.(\w+)", b["body"]) + orig_table = m.group(1) + if orig_table in DROP_TABLES: + dropped_table_reason += 1 + continue + new_table = rename_table(orig_table) + dropped_cols = set(COLUMN_CHANGES.get(new_table, {}).get("drop", [])) + # extract column list inside USING btree (...) + cm = re.search(r"USING \w+ \(([^)]+)\)", b["body"]) + cols_raw = cm.group(1) if cm else "" + # columns may include expressions; take simple identifiers only for the drop check + simple_cols = set(re.findall(r"\b\w+\b", cols_raw)) + if simple_cols & dropped_cols: + dropped_col_reason += 1 + print("DROPPED FOR COLUMN:", b["name"], b["body"].strip()) + continue + name = b["name"] + body = apply_table_renames(b["body"]) + if orig_table in RENAME_TABLES: + new_name = name.replace(orig_table, new_table) + body = re.sub(rf"\b{re.escape(name)}\b", new_name, body) + name = new_name + kept.append(body.strip()) + +with open(BASE / "sections" / "08-indexes-body.sql", "w") as f: + f.write("\n\n".join(kept) + "\n") + +print("Kept indexes:", len(kept), "of", len(index_blocks)) +print("Dropped (table reason):", dropped_table_reason) +print("Dropped (column reason):", dropped_col_reason) diff --git a/docs/plans/002-oss-migration/002b-task5-generation/gen_policies.py b/docs/plans/002-oss-migration/002b-task5-generation/gen_policies.py new file mode 100644 index 0000000..9ffdfe8 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/gen_policies.py @@ -0,0 +1,88 @@ +import json, re +from registry import DROP_TABLES, DROP_VIEWS, RENAME_TABLES, RENAME_FUNCTIONS, PARAMETERIZE_ROLES +from pathlib import Path + +BASE = Path(__file__).parent + +blocks = json.load(open(BASE / "blocks.json")) +policies = [b for b in blocks if b["type"] == "POLICY"] + +def table_of(b): + return b["name"].split(" ", 1)[0] + +def apply_table_renames(text): + for old, new in RENAME_TABLES.items(): + text = re.sub(rf"\bpublic\.{old}\b", f"public.{new}", text) + return text + +def apply_func_renames(text): + for old, new in RENAME_FUNCTIONS.items(): + text = re.sub(rf"\bpublic\.{old}\(\)", f"public.{new}()", text) + text = re.sub(rf"\bAS {old}\b", f"AS {new}", text) + return text + +# (table, policy_name) -> clause keyword to wrap ("USING" or "WITH CHECK") +WRAP_TARGETS = { + ("grids", "Allow NXT Grid to insert"): "WITH CHECK", + ("meters", "Allow NXT Grid to insert"): "WITH CHECK", + ("notes", "Allow NXT Grid to insert"): "WITH CHECK", + ("organizations", "Allow NXT Grid to insert"): "WITH CHECK", + ("pd_sites", "Allow NXT Grid to insert"): "WITH CHECK", + ("poles", "Allow NXT Grid to insert"): "WITH CHECK", + ("wallets", "Allow NXT Grid to insert"): "WITH CHECK", + ("accounts", "Allow NXT Grid to update"): "USING", + ("connections", "Allow NXT Grid to update"): "USING", + ("grids", "Allow NXT Grid to update"): "USING", + ("meters", "Allow NXT Grid to update"): "USING", + ("organizations", "Allow NXT Grid to update"): "USING", + ("pd_site_submissions", "Allow NXT Grid to update"): "USING", + ("pd_sites", "Allow NXT Grid to update"): "USING", + ("notes", "Allow org members to insert"): "WITH CHECK", + ("poles", "Allow org members to insert"): "WITH CHECK", + ("accounts", "Allow org members to update"): "USING", + ("orders", "Allow org members to update"): "USING", +} + +def wrap_clause(text, clause): + # text contains e.g. USING (public.rls_check_if_admin_org_member()); + # or WITH CHECK ((public.rls_get_member_org_id() = rls_organization_id)); + # Wrap the *inner* boolean expression in ( SELECT AS ). + pattern = re.compile(rf"{clause} \((.*)\);\s*$", re.DOTALL) + m = pattern.search(text.rstrip()) + if not m: + raise ValueError(f"Could not locate {clause} clause in: {text!r}") + inner = m.group(1) + # Alias: function name for a bare call, or the clause keyword lowercased for + # a composite boolean expr — matches the dump's own convention of aliasing + # by the wrapped function's name; for composite exprs there's no dump + # precedent, so we mirror the simple-call convention using the helper + # function name found inside. + fn_match = re.search(r"public\.(\w+)\(", inner) + alias = fn_match.group(1) if fn_match else "check" + replacement = f"{clause} (( SELECT {inner} AS {alias}))" + return text[: m.start()] + replacement + ";\n" + +out = [] +wrapped_count = 0 +for b in policies: + t = table_of(b) + if t in DROP_TABLES or t in DROP_VIEWS: + continue + if any(f"TO {r}" in b["body"] for r in PARAMETERIZE_ROLES): + continue + + policy_name = b["name"][len(t) + 1:] + text = apply_func_renames(apply_table_renames(b["body"].strip())) + renamed_t = RENAME_TABLES.get(t, t) + + key = (renamed_t, policy_name) + if key in WRAP_TARGETS: + text = wrap_clause(text, WRAP_TARGETS[key]) + wrapped_count += 1 + + out.append(text.rstrip()) + +with open(BASE / "sections" / "13-policies-body.sql", "w") as f: + f.write("\n\n".join(out) + "\n") + +print(f"Kept policies: {len(out)}, wrapped: {wrapped_count}") diff --git a/docs/plans/002-oss-migration/002b-task5-generation/gen_rls_enable.py b/docs/plans/002-oss-migration/002b-task5-generation/gen_rls_enable.py new file mode 100644 index 0000000..ae30e57 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/gen_rls_enable.py @@ -0,0 +1,23 @@ +import json, re +from registry import DROP_TABLES, RENAME_TABLES +from pathlib import Path + +BASE = Path(__file__).parent + +blocks = json.load(open(BASE / "blocks.json")) +rs = [b for b in blocks if b["type"] == "ROW SECURITY"] + +out = [] +for b in rs: + t = b["name"] + if t in DROP_TABLES: + continue + text = b["body"].strip() + for old, new in RENAME_TABLES.items(): + text = re.sub(rf"\bpublic\.{old}\b", f"public.{new}", text) + out.append(text) + +with open(BASE / "sections" / "14-rls-enable-body.sql", "w") as f: + f.write("\n".join(out) + "\n") + +print("ROW SECURITY statements emitted:", len(out)) diff --git a/docs/plans/002-oss-migration/002b-task5-generation/gen_sequences.py b/docs/plans/002-oss-migration/002b-task5-generation/gen_sequences.py new file mode 100644 index 0000000..8e79b08 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/gen_sequences.py @@ -0,0 +1,52 @@ +import json, re +from registry import DROP_TABLES, RENAME_TABLES, RENAME_SEQUENCES, rename_table +from pathlib import Path + +BASE = Path(__file__).parent + +blocks = json.load(open(BASE / "blocks.json")) + +def strip_owner(body): + lines = [l for l in body.split("\n") if not re.match(r"^ALTER (TABLE|SEQUENCE) .* OWNER TO ", l)] + return "\n".join(lines).strip("\n") + +def apply_renames(text): + for old, new in RENAME_TABLES.items(): + text = re.sub(rf"\bpublic\.{old}\b", f"public.{new}", text) + for old, new in RENAME_SEQUENCES.items(): + text = re.sub(rf"\bpublic\.{old}\b", f"public.{new}", text) + text = re.sub(rf"\b{old}\b", new, text) # bare mentions inside SEQUENCE NAME clauses etc. + return text + +seq_blocks = {b["name"]: b for b in blocks if b["type"] == "SEQUENCE"} +default_blocks = [b for b in blocks if b["type"] == "DEFAULT"] +owned_blocks = {b["name"]: b for b in blocks if b["type"] == "SEQUENCE OWNED BY"} +table_names_kept_original_order = [b["name"] for b in blocks if b["type"] == "TABLE" and b["name"] not in DROP_TABLES] + +out = [] +for tname in table_names_kept_original_order: + seq_name = f"{tname}_id_seq" + seq_block = seq_blocks.get(seq_name) + if seq_block is None: + continue # shouldn't happen for our 35 keep tables (all have an id seq, plain or identity) + body = strip_owner(seq_block["body"]) + body = apply_renames(body) + out.append(body.strip()) + default_block = None + for d in default_blocks: + if d["name"] == f"{tname} id": + default_block = d + break + if default_block is not None: + dbody = apply_renames(strip_owner(default_block["body"])) + out.append(dbody.strip()) + owned_block = owned_blocks.get(seq_name) + if owned_block is not None: + obody = apply_renames(strip_owner(owned_block["body"])) + out.append(obody.strip()) + +with open(BASE / "sections" / "05-sequences-body.sql", "w") as f: + f.write("\n\n".join(out) + "\n") + +print("Sequence-bearing kept tables:", len(table_names_kept_original_order)) +print("Wrote 05-sequences-body.sql") diff --git a/docs/plans/002-oss-migration/002b-task5-generation/gen_tables.py b/docs/plans/002-oss-migration/002b-task5-generation/gen_tables.py new file mode 100644 index 0000000..752d4f8 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/gen_tables.py @@ -0,0 +1,88 @@ +import json, re +from pathlib import Path +from registry import ( + DROP_TABLES, RENAME_TABLES, COLUMN_CHANGES, table_kept, rename_table, +) + +BASE = Path(__file__).parent + +blocks = json.load(open(BASE / "blocks.json")) +tables = [b for b in blocks if b["type"] == "TABLE"] + +# Capability grouping for section comments (matches the plan's own batch structure) +CAPABILITY = { + "accounts": "Platform core", "agents": "Platform core", "api_keys": "Platform core", + "grids": "Platform core", "organizations": "Platform core", "members": "Platform core", + "poles": "Platform core", "routers": "Platform core", "dcus": "Platform core", + "energy_cabins": "Production monitoring", "mppts": "Production monitoring", "solcast_cache": "Production monitoring", + "customers": "Metering", "connections": "Metering", "connection_requested_meters": "Metering", + "meters": "Metering", "meter_interactions": "Metering", "meter_commissionings": "Metering", + "metering_hardware_imports": "Metering", "metering_hardware_install_sessions": "Metering", + "meter_command_batches": "Metering", "meter_command_batch_executions": "Metering", + "ussd_sessions": "Metering", "ussd_session_hops": "Metering", + "banks": "Payments", "wallets": "Payments", "transactions": "Payments", "orders": "Payments", + "notifications": "Notifications", "notification_parameters": "Notifications", + "issues": "Field ops", "notes": "Field ops", "audits": "Field ops", + "pd_sites": "Field ops", "pd_site_submissions": "Field ops", +} + +def parse_columns(body): + start = body.index("(") + end = body.rindex(");") + inner = body[start + 1:end] + lines = [l for l in inner.strip("\n").split("\n")] + cols = [] + for l in lines: + has_comma = l.rstrip().endswith(",") + stripped = l.rstrip() + if has_comma: + stripped = stripped[:-1] + # leading whitespace + column name is first token + m = re.match(r"^(\s*)(\S+)(.*)$", stripped) + indent, colname, rest = m.group(1), m.group(2), m.group(3) + cols.append({"raw": l, "name": colname, "indent": indent, "rest": rest, "has_comma": has_comma}) + return cols + + +def rebuild(table_name, cols): + lines = [] + for i, c in enumerate(cols): + suffix = "," if i < len(cols) - 1 else "" + lines.append(f"{c['indent']}{c['name']}{c['rest']}{suffix}") + body = "\n".join(lines) + return f"CREATE TABLE public.{table_name} (\n{body}\n);" + + +out_sections = {} +order = [] +for b in tables: + name = b["name"] + if name in DROP_TABLES: + continue + cols = parse_columns(b["body"]) + new_name = rename_table(name) + changes = COLUMN_CHANGES.get(new_name, {"drop": [], "rename": {}}) + drop_set = set(changes["drop"]) + rename_map = changes["rename"] + new_cols = [] + for c in cols: + if c["name"] in drop_set: + continue + if c["name"] in rename_map: + c["name"] = rename_map[c["name"]] + new_cols.append(c) + stmt = rebuild(new_name, new_cols) + out_sections[new_name] = stmt + order.append(new_name) + +# Verify column counts against expectation +total_kept_cols = sum(len(parse_columns(b["body"])) - len(COLUMN_CHANGES.get(rename_table(b["name"]), {"drop": []}).get("drop", [])) for b in tables if b["name"] not in DROP_TABLES) +print("Kept tables:", len(order)) +print("Total kept columns (tables only):", total_kept_cols) + +with open(BASE / "sections" / "04-tables-body.sql", "w") as f: + for name in order: + f.write(out_sections[name]) + f.write("\n\n") + +print("Wrote 04-tables-body.sql") diff --git a/docs/plans/002-oss-migration/002b-task5-generation/gen_triggers.py b/docs/plans/002-oss-migration/002b-task5-generation/gen_triggers.py new file mode 100644 index 0000000..1b7f482 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/gen_triggers.py @@ -0,0 +1,52 @@ +import json, re +from registry import DROP_TABLES, RENAME_TABLES, DROP_TRIGGERS, RENAME_TRIGGERS, rename_table +from pathlib import Path + +BASE = Path(__file__).parent + +blocks = json.load(open(BASE / "blocks.json")) +trigger_blocks = [b for b in blocks if b["type"] == "TRIGGER"] + +def apply_table_renames(text): + for old, new in RENAME_TABLES.items(): + text = re.sub(rf"\bpublic\.{old}\b", f"public.{new}", text) + return text + +# The batch/batch-execution triggers also call the renamed function +FUNC_RENAME_IN_TRIGGER = { + "append_rls_organization_id_by_directive_batch_id": "append_rls_organization_id_by_meter_command_batch_id", +} + +out = [] +for b in trigger_blocks: + m = re.search(r"ON public\.(\w+)", b["body"]) + orig_table = m.group(1) + # block name is "
" — strip the table prefix + trigger_name = b["name"][len(orig_table):].strip() + if (trigger_name, orig_table) in DROP_TRIGGERS: + continue + if orig_table in DROP_TABLES: + # shouldn't happen given DROP_TRIGGERS above, but guard anyway + continue + body = apply_table_renames(b["body"]) + for old_fn, new_fn in FUNC_RENAME_IN_TRIGGER.items(): + body = re.sub(rf"\bpublic\.{old_fn}\b", f"public.{new_fn}", body) + name = trigger_name + if name in RENAME_TRIGGERS: + new_name = RENAME_TRIGGERS[name] + body = re.sub(rf"\b{re.escape(name)}\b", new_name, body, count=1) + name = new_name + out.append(body.strip()) + +# auth.users triggers — sourced from the legacy migration chain directly since +# a --schema=public dump doesn't capture triggers defined on auth.users. +out.append( + "CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();" +) +out.append( + "CREATE TRIGGER on_auth_user_updated AFTER UPDATE ON auth.users FOR EACH ROW EXECUTE FUNCTION public.handle_update_user();" +) + +with open(BASE / "sections" / "11-triggers-body.sql", "w") as f: + f.write("\n\n".join(out) + "\n") +print("Kept/added triggers:", len(out)) diff --git a/docs/plans/002-oss-migration/002b-task5-generation/gen_views.py b/docs/plans/002-oss-migration/002b-task5-generation/gen_views.py new file mode 100644 index 0000000..153d1f3 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/gen_views.py @@ -0,0 +1,31 @@ +import json, re +from registry import DROP_VIEWS, VIEW_COLUMN_CHANGES +from pathlib import Path + +BASE = Path(__file__).parent + +blocks = json.load(open(BASE / "blocks.json")) +view_blocks = [b for b in blocks if b["type"] == "VIEW"] + +def strip_owner(body): + lines = [l for l in body.split("\n") if not re.match(r"^ALTER VIEW .* OWNER TO ", l)] + return "\n".join(lines).strip("\n") + +out = [] +for b in view_blocks: + if b["name"] in DROP_VIEWS: + continue + body = strip_owner(b["body"]) + drop_cols = VIEW_COLUMN_CHANGES.get(b["name"], {}).get("drop", []) + for col in drop_cols: + lines = body.split("\n") + new_lines = [l for l in lines if l.strip() != f"m.{col}," and l.strip() != f"m.{col}"] + removed = len(lines) - len(new_lines) + if removed == 0: + print(f"WARNING: column {col} not found as a selected line in view {b['name']}") + body = "\n".join(new_lines) + out.append(body.strip()) + +with open(BASE / "sections" / "09-views-body.sql", "w") as f: + f.write("\n\n".join(out) + "\n") +print("Kept views:", len(out)) diff --git a/docs/plans/002-oss-migration/002b-task5-generation/parse_dump.py b/docs/plans/002-oss-migration/002b-task5-generation/parse_dump.py new file mode 100644 index 0000000..906e734 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/parse_dump.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +"""Parse the pg_dump schema-only output into named blocks, keyed by +(type, schema, name) as pg_dump's own '-- Name: X; Type: Y; Schema: Z; Owner: W' +comment headers describe them. This lets Task 5 authoring operate on exact +dump text (no retyping) and only apply targeted edits. +""" +import re +import sys +import json +from pathlib import Path + +BASE = Path(__file__).parent + +with open(BASE / "schema-reference.sql") as f: + text = f.read() + +# Header pattern used throughout pg_dump output. +header_re = re.compile( + r"^--\n-- Name: (?P.*?); Type: (?P.*?); Schema: (?P.*?); Owner: (?P.*?)\n--\n", + re.MULTILINE, +) + +matches = list(header_re.finditer(text)) +blocks = [] +for i, m in enumerate(matches): + start = m.end() + end = matches[i + 1].start() if i + 1 < len(matches) else len(text) + body = text[start:end].rstrip("\n") + blocks.append({ + "name": m.group("name"), + "type": m.group("type"), + "schema": m.group("schema"), + "owner": m.group("owner"), + "body": body, + }) + +print(f"Total blocks: {len(blocks)}", file=sys.stderr) + +with open(BASE / "blocks.json", "w") as f: + json.dump(blocks, f, indent=1) + +# Sanity: also capture the preamble (before first header) for inspection. +preamble = text[: matches[0].start()] if matches else text +with open(BASE / "sections" / "preamble.sql", "w") as f: + f.write(preamble) + +# Summary by type +from collections import Counter +c = Counter(b["type"] for b in blocks) +for t, n in sorted(c.items()): + print(f"{t}: {n}", file=sys.stderr) diff --git a/docs/plans/002-oss-migration/002b-task5-generation/registry.py b/docs/plans/002-oss-migration/002b-task5-generation/registry.py new file mode 100644 index 0000000..8c82504 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/registry.py @@ -0,0 +1,157 @@ +"""Authoritative keep/drop/rename registry for Task 5, derived from +docs/plans/002-oss-migration/002b-schema-deviation-register.md. +""" + +DROP_TABLES = { + "directives", "lorawan_directives", "meter_credit_transfers", + "directive_watchdog_sessions", "features", "member_feature", "migrations", + "devices", "device_types", "device_logs", "payouts", "bank_accounts", + "pd_flows", "pd_flow_templates", "pd_sections", "pd_section_templates", + "pd_actions", "pd_action_templates", "pd_documents", "pd_document_templates", + "pd_audits", "autopilot_executions", +} + +RENAME_TABLES = { + "directive_batches": "meter_command_batches", + "directive_batch_executions": "meter_command_batch_executions", +} + +DROP_ENUMS = { + "directive_direction_enum", "directive_error_enum", "directive_phase_enum", + "directive_special_status_enum", "directive_status_enum", "directive_type_enum", + "meter_credit_transfer_status_enum", "payout_status_enum", + "pd_action_status_enum", "pd_action_type_enum", "pd_document_type_enum", +} + +DROP_VIEWS = {"batch_commands"} +KEEP_VIEWS = {"agents_with_account", "customers_with_account", "meters_with_account_and_statuses"} + +DROP_FUNCTIONS = { + "append_rls_organization_id_by_device_id", + "append_rls_organization_id_by_receiver_meter_id", + "append_rls_organization_id_by_historical_grid_id", + "lock_next_order", + "lock_next_pd_action", + "notify_make_about_is_fs_on_updated", + "notify_make_about_is_hps_on_updated", + "notify_make_about_kwh_tariff_essential_service_updated", +} + +RENAME_FUNCTIONS = { + "append_rls_organization_id_by_directive_batch_id": "append_rls_organization_id_by_meter_command_batch_id", + "rls_check_if_nxt_member": "rls_check_if_admin_org_member", +} + +# trigger name -> table it's defined on, used to disambiguate the one trigger +# whose name collides with a function name (append_rls_organization_id_by_device_id) +DROP_TRIGGERS = { + ("append_rls_organization_id_by_device_id", "device_logs"), + ("append_rls_organization_id_on_devices_insert", "devices"), + ("append_rls_organization_id_on_directive_insert", "directives"), + ("append_rls_organization_id_on_lorawan_directive_insert", "lorawan_directives"), + ("append_rls_organization_id_on_meter_credit_transfer_insert", "meter_credit_transfers"), + ("notify_make_about_is_fs_on_updated", "grids"), + ("notify_make_about_is_hps_on_updated", "grids"), + ("notify_make_about_kwh_tariff_essential_service_updated", "grids"), +} + +RENAME_TRIGGERS = { + "append_rls_organization_id_on_directive_batch_insert": "append_rls_organization_id_on_meter_command_batch_insert", + "append_rls_organization_id_on_directive_batch_execution_insert": "append_rls_organization_id_on_meter_command_batch_execution_insert", + "append_rls_organization_id_on_route_insert": "append_rls_organization_id_on_router_insert", +} + +DROP_SEQUENCES = { + "bank_accounts_id_seq", "directive_watchdog_sessions_id_seq", "directives_id_seq", + "features_id_seq", "meter_credit_transfers_id_seq", "migrations_id_seq", + "payouts_id_seq", + # identity-column sequences on dropped tables (auto-dropped with the table) + "autopilot_executions_id_seq", "device_logs_id_seq", "device_types_id_seq", + "devices_id_seq", "lorawan_directives_id_seq", "pd_action_templates_id_seq", + "pd_actions_id_seq", "pd_audits_id_seq", "pd_document_templates_id_seq", + "pd_documents_id_seq", "pd_flow_templates_id_seq", "pd_flows_id_seq", + "pd_section_templates_id_seq", "pd_sections_id_seq", +} + +RENAME_SEQUENCES = { + "directive_batches_id_seq": "meter_command_batches_id_seq", + "directive_batch_executions_id_seq": "meter_command_batch_executions_id_seq", +} + +# Column-level drops/renames on KEPT tables/views, from the deviation register +# "Column adjustments" section (post-rename table names used as keys). +COLUMN_CHANGES = { + "orders": { + "drop": ["directive_id", "lorawan_directive_id", "meter_credit_transfer_id", "external_system"], + "rename": {}, + }, + "meter_command_batches": { + "drop": ["directive_type", "lock_session", "execution_bucket"], + "rename": {}, + }, + "meter_command_batch_executions": { + "drop": [], + "rename": {"directive_batch_id": "meter_command_batch_id"}, + }, + "meters": { + "drop": [ + "current_special_status", "device_id", "watchdog_session", + "watchdog_last_run_at", "power_down_count", "power_down_count_updated_at", + "is_simulated", "pulse_counter_kwh", "pulse_counter_kwh_updated_at", + ], + "rename": {}, + }, + "grids": { + "drop": [ + "is_automatic_payout_generation_enabled", "are_all_dcus_online", + "are_all_dcus_under_high_load_threshold", + "meter_consumption_issue_threshold_detection_days", + "meter_communication_issue_threshold_detection_days", + "uses_dual_meter_setup", "telegram_response_path_autopilot", + ], + "rename": {}, + }, + "pd_sites": {"drop": ["pd_flow_id"], "rename": {}}, + "organizations": {"drop": ["pd_hero_google_drive_folder_id", "phone", "address"], "rename": {}}, + "api_keys": {"drop": ["is_locked"], "rename": {}}, + "dcus": {"drop": ["queue_buffer_length"], "rename": {}}, + "meter_commissionings": { + "drop": [ + "initialised_steps", "pending_steps", "processing_steps", + "successful_steps", "failed_steps", "total_steps", "lock_session", + ], + "rename": {}, + }, + "wallets": {"drop": ["goldring_migration_id"], "rename": {}}, + "issues": { + "drop": ["estimated_lost_revenue", "snoozed_until", "mppt_id", "grid_id"], + "rename": {"external_system": "external_tracking_system", "external_reference": "external_tracking_reference"}, + }, +} + +# View column changes (same underlying columns, view is a separate object) +VIEW_COLUMN_CHANGES = { + "meters_with_account_and_statuses": { + "drop": [ + "current_special_status", "device_id", "watchdog_session", + "watchdog_last_run_at", "power_down_count", "power_down_count_updated_at", + "is_simulated", "pulse_counter_kwh", "pulse_counter_kwh_updated_at", + ], + }, +} + +EXTERNAL_SYSTEM_ENUM_DROP_VALUES = {"JOTFORM", "STEAMACO", "ACREL"} +NOTIFICATION_TYPE_ENUM_DROP_VALUES = {"AUTO_PAYOUT_GENRATION_REPORT"} + +# Readonly / company-infra roles parameterized out entirely (register #2/#3/#5/#6) +PARAMETERIZE_ROLES = {"grafana_readonly", "make_readonly", "snaplet_readonly_2"} + +DATA_API_ROLES = {"anon", "authenticated", "service_role"} + + +def table_kept(name: str) -> bool: + return name not in DROP_TABLES + + +def rename_table(name: str) -> str: + return RENAME_TABLES.get(name, name) diff --git a/docs/plans/002-oss-migration/002b-task5-generation/schema-reference.sql b/docs/plans/002-oss-migration/002b-task5-generation/schema-reference.sql new file mode 100644 index 0000000..3702f96 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/schema-reference.sql @@ -0,0 +1,9524 @@ +-- +-- PostgreSQL database dump +-- + +-- Dumped from database version 15.8 +-- Dumped by pg_dump version 17.0 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET transaction_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; + +-- +-- Name: public; Type: SCHEMA; Schema: -; Owner: pg_database_owner +-- + +CREATE SCHEMA public; + + +ALTER SCHEMA public OWNER TO pg_database_owner; + +-- +-- Name: SCHEMA public; Type: COMMENT; Schema: -; Owner: pg_database_owner +-- + +COMMENT ON SCHEMA public IS 'standard public schema'; + + +-- +-- Name: account_type_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.account_type_enum AS ENUM ( + 'AGENT', + 'MEMBER', + 'CUSTOMER' +); + + +ALTER TYPE public.account_type_enum OWNER TO postgres; + +-- +-- Name: communication_protocol_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.communication_protocol_enum AS ENUM ( + 'CALIN_V1', + 'CALIN_V2', + 'CALIN_LORAWAN' +); + + +ALTER TYPE public.communication_protocol_enum OWNER TO postgres; + +-- +-- Name: currency_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.currency_enum AS ENUM ( + 'USD', + 'NGN', + 'EUR' +); + + +ALTER TYPE public.currency_enum OWNER TO postgres; + +-- +-- Name: directive_direction_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.directive_direction_enum AS ENUM ( + 'UP', + 'DOWN' +); + + +ALTER TYPE public.directive_direction_enum OWNER TO postgres; + +-- +-- Name: directive_error_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.directive_error_enum AS ENUM ( + 'GRID_DOWN', + 'DCU_OFFLINE', + 'NO_METER', + 'NO_DCU', + 'NO_GRID', + 'NO_CONNECTION', + 'NO_CUSTOMER' +); + + +ALTER TYPE public.directive_error_enum OWNER TO postgres; + +-- +-- Name: directive_phase_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.directive_phase_enum AS ENUM ( + 'A', + 'B', + 'C' +); + + +ALTER TYPE public.directive_phase_enum OWNER TO postgres; + +-- +-- Name: directive_special_status_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.directive_special_status_enum AS ENUM ( + 'POWER_LIMIT_BREACHED', + 'CREDIT_EXHAUSTED', + 'REMOTE_SWITCHED_OFF', + 'OVER_VOLTAGE', + 'METER_NOT_ACTIVATED', + 'TAMPER', + 'LOW_VOLTAGE' +); + + +ALTER TYPE public.directive_special_status_enum OWNER TO postgres; + +-- +-- Name: directive_status_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.directive_status_enum AS ENUM ( + 'INITIALISED', + 'PENDING', + 'SENT_TO_API', + 'RECEIVED_BY_API', + 'SENT_TO_DCU', + 'RECEIVED_BY_DCU', + 'SENT_TO_METER', + 'RECEIVED_BY_METER', + 'SUCCESSFUL', + 'FAILED', + 'IGNORED', + 'CANCELLED', + 'TIMED_OUT', + 'UNKNOWN' +); + + +ALTER TYPE public.directive_status_enum OWNER TO postgres; + +-- +-- Name: directive_type_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.directive_type_enum AS ENUM ( + 'ON', + 'OFF', + 'READ_VOLTAGE', + 'PLS', + 'PLR', + 'CLEAR_TAMPER', + 'READ_METER_VERSION', + 'TOP_UP', + 'READ_CURRENT_CREDIT', + 'READ_CURRENT', + 'READ_POWER', + 'READ_SPECIAL_STATUS', + 'CLEAR_CREDIT', + 'READ_RELAY_STATUS', + 'READ_POWER_DOWN_COUNT', + 'READ_TIME', + 'READ_DATE', + 'READ_VOLTAGE_A', + 'READ_VOLTAGE_B', + 'READ_VOLTAGE_C', + 'READ_POWER_A', + 'READ_POWER_B', + 'READ_POWER_C', + 'READ_CURRENT_A', + 'READ_CURRENT_B', + 'READ_CURRENT_C', + 'READ_TOTAL_ACTIVE_KWH', + 'READ_STATUS', + 'SEND_TOKEN', + 'READ_FRAUD_STATUS', + 'READ_REPORT', + 'WRITE_DATE', + 'WRITE_TIME', + 'CLEAR_TAMPER_TOKEN', + 'READ_REPORT_UP', + 'READ_REPORT_DOWN', + 'READ_CREDIT_DOWN', + 'READ_CREDIT_UP', + 'READ_VOLTAGE_UP', + 'READ_VOLTAGE_DOWN', + 'READ_POWER_UP', + 'READ_POWER_DOWN', + 'READ_CURRENT_UP', + 'READ_CURRENT_DOWN', + 'CLEAR_TAMPER_UP', + 'CLEAR_TAMPER_DOWN', + 'TOP_UP_DOWN', + 'POWER_LIMIT_SET_UP', + 'POWER_LIMIT_SET_DOWN', + 'OPEN_RELAY_UP', + 'OPEN_RELAY_DOWN', + 'CLOSE_RELAY_UP', + 'CLOSE_RELAY_DOWN', + 'READ_VOLTAGE_A_UP', + 'READ_VOLTAGE_A_DOWN', + 'READ_POWER_A_UP', + 'READ_POWER_A_DOWN', + 'READ_CURRENT_A_UP', + 'READ_CURRENT_A_DOWN', + 'UNKNOWN', + 'CLEAR_CREDIT_DOWN', + 'TOP_UP_KWH', + 'TOKEN_ACCEPTED', + 'TOKEN_REJECTED', + 'ON_OFF_ACCEPTED', + 'ON_OFF_REJECTED' +); + + +ALTER TYPE public.directive_type_enum OWNER TO postgres; + +-- +-- Name: external_system_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.external_system_enum AS ENUM ( + 'STEAMACO', + 'CALIN', + 'SOLCAST', + 'VICTRON', + 'FLUTTERWAVE', + 'AFRICASTALKING', + 'JOTFORM', + 'EPICOLLECT', + 'JIRA', + 'TELEGRAM', + 'ZEROTIER', + 'MAKE', + 'FLOW_XO', + 'SENDGRID', + 'ACREL' +); + + +ALTER TYPE public.external_system_enum OWNER TO postgres; + +-- +-- Name: fs_command_type_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.fs_command_type_enum AS ENUM ( + 'ON', + 'OFF' +); + + +ALTER TYPE public.fs_command_type_enum OWNER TO postgres; + +-- +-- Name: gender_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.gender_enum AS ENUM ( + 'MALE', + 'FEMALE' +); + + +ALTER TYPE public.gender_enum OWNER TO postgres; + +-- +-- Name: generator_type_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.generator_type_enum AS ENUM ( + 'SMALL', + 'LARGE' +); + + +ALTER TYPE public.generator_type_enum OWNER TO postgres; + +-- +-- Name: id_document_type_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.id_document_type_enum AS ENUM ( + 'PASSPORT', + 'NATIONAL_ID', + 'DRIVING_LICENSE', + 'VOTERS_CARD' +); + + +ALTER TYPE public.id_document_type_enum OWNER TO postgres; + +-- +-- Name: issue_status_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.issue_status_enum AS ENUM ( + 'OPEN', + 'CLOSED', + 'OVERRIDDEN' +); + + +ALTER TYPE public.issue_status_enum OWNER TO postgres; + +-- +-- Name: issue_type_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.issue_type_enum AS ENUM ( + 'NO_COMMUNICATION', + 'METER_NOT_ACTIVATED', + 'TAMPER', + 'POWER_LIMIT_BREACHED', + 'OVER_VOLTAGE', + 'LOW_VOLTAGE', + 'POWER_LIMIT_BAD_CONFIGURATION', + 'METER_STATE_BAD_CONFIGURATION', + 'UNEXPECTED_POWER_LIMIT', + 'UNEXPECTED_METER_STATUS', + 'NO_CREDIT', + 'NO_CONSUMPTION', + 'NUMBER_OF_PHASES', + 'VEBUS_STATE', + 'VEBUS_ERROR', + 'QUATTRO_TEMPERATURE_ALARM', + 'QUATTRO_OVERLOAD_ALARM', + 'HIGH_BATTERY_TEMPERATURE_ALARM', + 'CELL_IMBALANCE_ALARM', + 'HIGH_CHARGE_CURRENT_ALARM', + 'HIGH_CHARGE_TEMPERATURE_ALARM', + 'BATTERY_INTERNAL_FAILURE', + 'BATTERY_CHARGE_BLOCKED_ALARM', + 'BATTERY_DISCHARGE_BLOCKED_ALARM' +); + + +ALTER TYPE public.issue_type_enum OWNER TO postgres; + +-- +-- Name: member_type_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.member_type_enum AS ENUM ( + 'SUPERADMIN', + 'ADMIN', + 'PARTNER', + 'FINANCE', + 'DEVELOPER', + 'MANAGER', + 'SUPPORT', + 'SERVICE', + 'SALES', + 'TECH' +); + + +ALTER TYPE public.member_type_enum OWNER TO postgres; + +-- +-- Name: meter_commissioning_status_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.meter_commissioning_status_enum AS ENUM ( + 'PENDING', + 'PROCESSING', + 'SUCCESSFUL', + 'FAILED' +); + + +ALTER TYPE public.meter_commissioning_status_enum OWNER TO postgres; + +-- +-- Name: meter_credit_transfer_status_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.meter_credit_transfer_status_enum AS ENUM ( + 'PENDING', + 'PROCESSING', + 'SUCCESSFUL', + 'FAILED' +); + + +ALTER TYPE public.meter_credit_transfer_status_enum OWNER TO postgres; + +-- +-- Name: meter_interaction_status_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.meter_interaction_status_enum AS ENUM ( + 'QUEUED', + 'ABORTED', + 'PROCESSING', + 'SUCCESSFUL', + 'FAILED', + 'DEFERRED', + 'SUSPENDED' +); + + +ALTER TYPE public.meter_interaction_status_enum OWNER TO postgres; + +-- +-- Name: meter_interaction_type_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.meter_interaction_type_enum AS ENUM ( + 'READ_CREDIT', + 'READ_POWER_LIMIT', + 'READ_VOLTAGE', + 'SET_POWER_LIMIT', + 'TOP_UP', + 'TURN_ON', + 'TURN_OFF', + 'READ_POWER', + 'READ_CURRENT', + 'CLEAR_CREDIT', + 'CLEAR_TAMPER', + 'READ_REPORT', + 'JOIN_NETWORK', + 'DELIVER_PREEXISTING_TOKEN', + 'READ_VERSION', + 'READ_DATE', + 'SET_DATE', + 'READ_TIME', + 'SET_TIME' +); + + +ALTER TYPE public.meter_interaction_type_enum OWNER TO postgres; + +-- +-- Name: meter_phase_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.meter_phase_enum AS ENUM ( + 'SINGLE_PHASE', + 'THREE_PHASE' +); + + +ALTER TYPE public.meter_phase_enum OWNER TO postgres; + +-- +-- Name: meter_type_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.meter_type_enum AS ENUM ( + 'HPS', + 'FS' +); + + +ALTER TYPE public.meter_type_enum OWNER TO postgres; + +-- +-- Name: mhi_operation_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.mhi_operation_enum AS ENUM ( + 'ADD', + 'REMOVE' +); + + +ALTER TYPE public.mhi_operation_enum OWNER TO postgres; + +-- +-- Name: mhi_status_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.mhi_status_enum AS ENUM ( + 'PENDING', + 'PROCESSING', + 'SUCCESSFUL', + 'FAILED' +); + + +ALTER TYPE public.mhi_status_enum OWNER TO postgres; + +-- +-- Name: mppt_type_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.mppt_type_enum AS ENUM ( + 'MPPT', + 'PV_INVERTER' +); + + +ALTER TYPE public.mppt_type_enum OWNER TO postgres; + +-- +-- Name: notification_status_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.notification_status_enum AS ENUM ( + 'PENDING', + 'PROCESSING', + 'RECEIVED_BY_API', + 'FAILED', + 'SUCCESSFUL', + 'READ', + 'UNKNOWN' +); + + +ALTER TYPE public.notification_status_enum OWNER TO postgres; + +-- +-- Name: notification_type_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.notification_type_enum AS ENUM ( + 'GRID_IS_HPS_ON_STATE_CHANGE', + 'GRID_IS_FS_ON_STATE_CHANGE', + 'GRID_METERING_HARDWARE_STATE_CHANGE', + 'FS_RULE_EXECUTION_COMING_UP', + 'FS_RULE_CHANGED', + 'TARIFF_RULE_CHANGED', + 'CLEAN_PANELS_REMINDER', + 'GRID_REVENUE', + 'PASSWORD_RESET', + 'INVITE', + 'AUTO_PAYOUT_GENRATION_REPORT', + 'CREDIT_SENT', + 'CREDIT_RECEIVED', + 'METER_TOPPED_UP', + 'PAYMENT_REJECTED', + 'SITE_SUBMISSION' +); + + +ALTER TYPE public.notification_type_enum OWNER TO postgres; + +-- +-- Name: order_actor_type_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.order_actor_type_enum AS ENUM ( + 'BANKING_SYSTEM', + 'ORGANIZATION', + 'CONNECTION', + 'METER', + 'AGENT', + 'CUSTOMER' +); + + +ALTER TYPE public.order_actor_type_enum OWNER TO postgres; + +-- +-- Name: order_status_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.order_status_enum AS ENUM ( + 'INITIALISED', + 'PENDING', + 'COMPLETED', + 'FAILED', + 'CANCELLED', + 'TIMED_OUT', + 'IGNORED' +); + + +ALTER TYPE public.order_status_enum OWNER TO postgres; + +-- +-- Name: order_type_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.order_type_enum AS ENUM ( + 'ENERGY_TOPUP', + 'CONNECTION_PAYMENT', + 'CONNECTION_REFUND', + 'AGENT_WITHDRAWAL', + 'AGENT_TOPUP', + 'ORGANIZATION_TOPUP', + 'ORGANIZATION_WITHDRAWAL', + 'CUSTOMER_TOPUP' +); + + +ALTER TYPE public.order_type_enum OWNER TO postgres; + +-- +-- Name: organization_type_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.organization_type_enum AS ENUM ( + 'SOLAR_DEVELOPER', + 'LENDER', + 'DATA_AGGREGATOR' +); + + +ALTER TYPE public.organization_type_enum OWNER TO postgres; + +-- +-- Name: payment_channel_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.payment_channel_enum AS ENUM ( + 'USSD', + 'AYRTON', + 'NIFFLER', + 'TELEGRAM' +); + + +ALTER TYPE public.payment_channel_enum OWNER TO postgres; + +-- +-- Name: payment_method_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.payment_method_enum AS ENUM ( + 'CREDIT_CARD', + 'USSD', + 'BANK_TRANSFER' +); + + +ALTER TYPE public.payment_method_enum OWNER TO postgres; + +-- +-- Name: payout_status_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.payout_status_enum AS ENUM ( + 'INITIALISED', + 'WAITING_FOR_APPROVAL', + 'PROCESSING', + 'SUCCESSFUL', + 'FAILED' +); + + +ALTER TYPE public.payout_status_enum OWNER TO postgres; + +-- +-- Name: pd_action_status_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.pd_action_status_enum AS ENUM ( + 'GENERATING', + 'GENERATION_FAILED', + 'GENERATION_COMPLETED', + 'ACTIONABLE', + 'ACTION_COMPLETED' +); + + +ALTER TYPE public.pd_action_status_enum OWNER TO postgres; + +-- +-- Name: pd_action_type_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.pd_action_type_enum AS ENUM ( + 'UPLOAD', + 'TEMPLATE', + 'EXTERNAL', + 'START', + 'END' +); + + +ALTER TYPE public.pd_action_type_enum OWNER TO postgres; + +-- +-- Name: pd_document_type_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.pd_document_type_enum AS ENUM ( + 'GOOGLE_SHEETS', + 'GOOGLE_DOCS' +); + + +ALTER TYPE public.pd_document_type_enum OWNER TO postgres; + +-- +-- Name: solcast_cache_request_type_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.solcast_cache_request_type_enum AS ENUM ( + 'ESTIMATED_ACTUALS', + 'FORECAST' +); + + +ALTER TYPE public.solcast_cache_request_type_enum OWNER TO postgres; + +-- +-- Name: transaction_status_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.transaction_status_enum AS ENUM ( + 'SUCCESSFUL', + 'FAILED' +); + + +ALTER TYPE public.transaction_status_enum OWNER TO postgres; + +-- +-- Name: wallet_type_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.wallet_type_enum AS ENUM ( + 'VIRTUAL', + 'REAL' +); + + +ALTER TYPE public.wallet_type_enum OWNER TO postgres; + +-- +-- Name: weather_type_enum; Type: TYPE; Schema: public; Owner: postgres +-- + +CREATE TYPE public.weather_type_enum AS ENUM ( + 'CLOUDY', + 'CLOUDS', + 'SHOWERS', + 'SUNNY', + 'UNKNOWN', + 'CLOUDY_WITH_RAIN' +); + + +ALTER TYPE public.weather_type_enum OWNER TO postgres; + +-- +-- Name: append_rls_organization_id_by_account_id(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.append_rls_organization_id_by_account_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + SELECT + organization_id + FROM + accounts + LEFT JOIN organizations ON organizations.id = accounts.organization_id INTO NEW.rls_organization_id +WHERE + accounts.id = new.account_id; + RETURN NEW; +END; +$$; + + +ALTER FUNCTION public.append_rls_organization_id_by_account_id() OWNER TO postgres; + +-- +-- Name: append_rls_organization_id_by_connection_id(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.append_rls_organization_id_by_connection_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + SELECT + organization_id + FROM + connections + LEFT JOIN customers ON customers.id = connections.customer_id + LEFT JOIN grids ON customers.grid_id = grids.id INTO NEW.rls_organization_id +WHERE + connections.id = new.connection_id; + RETURN NEW; +END; +$$; + + +ALTER FUNCTION public.append_rls_organization_id_by_connection_id() OWNER TO postgres; + +-- +-- Name: append_rls_organization_id_by_customer_id(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.append_rls_organization_id_by_customer_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + SELECT + organization_id + FROM + public.customers + LEFT JOIN public.grids ON customers.grid_id = grids.id INTO NEW.rls_organization_id +WHERE + customers.id = new.customer_id; + RETURN NEW; +END; +$$; + + +ALTER FUNCTION public.append_rls_organization_id_by_customer_id() OWNER TO postgres; + +-- +-- Name: append_rls_organization_id_by_customer_id_or_agent_id_or_connec(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.append_rls_organization_id_by_customer_id_or_agent_id_or_connec() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + rls_organization_id integer; +BEGIN + IF NEW.customer_id IS NOT NULL THEN + SELECT + organization_id INTO rls_organization_id + FROM + customers + LEFT JOIN grids ON customers.grid_id = grids.id + WHERE + customers.id = NEW.customer_id; + ELSIF NEW.agent_id IS NOT NULL THEN + SELECT + organization_id INTO rls_organization_id + FROM + agents + LEFT JOIN grids ON agents.grid_id = grids.id + WHERE + agents.id = NEW.agent_id; + ELSIF NEW.connection_id IS NOT NULL THEN + SELECT + organization_id INTO rls_organization_id + FROM + connections + LEFT JOIN customers ON customers.id = connections.customer_id + LEFT JOIN grids ON customers.grid_id = grids.id + WHERE + connections.id = NEW.connection_id; + ELSIF NEW.organization_id IS NOT NULL THEN + SELECT + id INTO rls_organization_id + FROM + organizations + WHERE + organizations.id = NEW.organization_id; + ELSIF NEW.meter_id IS NOT NULL THEN + SELECT + organization_id INTO rls_organization_id + FROM + meters + LEFT JOIN connections ON connections.id = meters.connection_id + LEFT JOIN customers ON customers.id = connections.customer_id + LEFT JOIN grids ON customers.grid_id = grids.id + WHERE + meters.id = NEW.meter_id; + END IF; + NEW.rls_organization_id = rls_organization_id; + RETURN NEW; +END; +$$; + + +ALTER FUNCTION public.append_rls_organization_id_by_customer_id_or_agent_id_or_connec() OWNER TO postgres; + +-- +-- Name: append_rls_organization_id_by_dcu_id_or_meter_id(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.append_rls_organization_id_by_dcu_id_or_meter_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$DECLARE + rls_organization_id integer; +BEGIN + IF new.dcu_id IS NOT NULL THEN + SELECT + organization_id + FROM + dcus + LEFT JOIN grids ON dcus.grid_id = grids.id INTO rls_organization_id + WHERE + dcus.id = new.dcu_id; + elseif new.meter_id IS NOT NULL THEN + SELECT + organization_id + FROM + meters + LEFT JOIN connections ON connections.id = meters.connection_id + LEFT JOIN customers ON customers.id = connections.customer_id + LEFT JOIN grids ON customers.grid_id = grids.id INTO rls_organization_id + WHERE + meters.id = new.meter_id; + END IF; + NEW.rls_organization_id = rls_organization_id; + RETURN NEW; +END;$$; + + +ALTER FUNCTION public.append_rls_organization_id_by_dcu_id_or_meter_id() OWNER TO postgres; + +-- +-- Name: append_rls_organization_id_by_device_id(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.append_rls_organization_id_by_device_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$BEGIN + SELECT + organization_id + FROM + devices + LEFT JOIN grids ON grids.id = devices.grid_id + INTO NEW.rls_organization_id +WHERE + devices.id = new.device_id; + RETURN NEW; +END;$$; + + +ALTER FUNCTION public.append_rls_organization_id_by_device_id() OWNER TO postgres; + +-- +-- Name: append_rls_organization_id_by_directive_batch_id(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.append_rls_organization_id_by_directive_batch_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + SELECT + organization_id + FROM + directive_batches + LEFT JOIN grids ON grids.id = directive_batches.grid_id INTO NEW.rls_organization_id +WHERE + directive_batches.id = new.directive_batch_id; + RETURN NEW; +END; +$$; + + +ALTER FUNCTION public.append_rls_organization_id_by_directive_batch_id() OWNER TO postgres; + +-- +-- Name: append_rls_organization_id_by_grid_id(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.append_rls_organization_id_by_grid_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + SELECT + organization_id + FROM + grids INTO NEW.rls_organization_id + WHERE + grids.id = new.grid_id; + RETURN NEW; +END; +$$; + + +ALTER FUNCTION public.append_rls_organization_id_by_grid_id() OWNER TO postgres; + +-- +-- Name: append_rls_organization_id_by_historical_grid_id(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.append_rls_organization_id_by_historical_grid_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + SELECT + organization_id + FROM + grids INTO NEW.rls_organization_id + WHERE + grids.id = new.historical_grid_id; + RETURN NEW; +END; +$$; + + +ALTER FUNCTION public.append_rls_organization_id_by_historical_grid_id() OWNER TO postgres; + +-- +-- Name: append_rls_organization_id_by_meter_id(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.append_rls_organization_id_by_meter_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$BEGIN + SELECT + organization_id + FROM meters + LEFT JOIN connections ON connections.id = meters.connection_id + LEFT JOIN customers ON customers.id = connections.customer_id + LEFT JOIN grids ON customers.grid_id = grids.id INTO NEW.rls_organization_id +WHERE + meters.id = new.meter_id; + RETURN NEW; +END;$$; + + +ALTER FUNCTION public.append_rls_organization_id_by_meter_id() OWNER TO postgres; + +-- +-- Name: append_rls_organization_id_by_metering_hardware_install_session(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.append_rls_organization_id_by_metering_hardware_install_session() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$BEGIN + SELECT + organization_id + FROM metering_hardware_install_sessions + left join meters on meters.id = metering_hardware_install_sessions.meter_id + LEFT JOIN connections ON connections.id = meters.connection_id + LEFT JOIN customers ON customers.id = connections.customer_id + LEFT JOIN grids ON customers.grid_id = grids.id INTO NEW.rls_organization_id +WHERE + metering_hardware_install_sessions.id = new.metering_hardware_install_session_id; + RETURN NEW; +END;$$; + + +ALTER FUNCTION public.append_rls_organization_id_by_metering_hardware_install_session() OWNER TO postgres; + +-- +-- Name: append_rls_organization_id_by_order_id(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.append_rls_organization_id_by_order_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + SELECT + organization_id + FROM + orders + LEFT JOIN grids ON historical_grid_id = grids.id INTO NEW.rls_organization_id +WHERE + orders.id = new.order_id; + RETURN NEW; +END; +$$; + + +ALTER FUNCTION public.append_rls_organization_id_by_order_id() OWNER TO postgres; + +-- +-- Name: append_rls_organization_id_by_receiver_meter_id(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.append_rls_organization_id_by_receiver_meter_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + SELECT + organization_id + FROM + meters + LEFT JOIN connections ON connections.id = meters.connection_id + LEFT JOIN customers ON customers.id = connections.customer_id + LEFT JOIN grids ON customers.grid_id = grids.id INTO NEW.rls_organization_id +WHERE + meters.id = new.receiver_meter_id; + RETURN NEW; +END; +$$; + + +ALTER FUNCTION public.append_rls_organization_id_by_receiver_meter_id() OWNER TO postgres; + +-- +-- Name: find_energy_topup_revenue(integer, timestamp with time zone, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.find_energy_topup_revenue(grid_id integer, start_date timestamp with time zone, end_date timestamp with time zone) RETURNS numeric + LANGUAGE plpgsql + SET search_path TO '' + AS $$ +DECLARE + total_revenue NUMERIC; +BEGIN + SELECT + SUM(amount) INTO total_revenue + FROM + public.orders + WHERE + meta_order_type = 'ENERGY_TOPUP'::public.order_type_enum + AND order_status = 'COMPLETED'::public.order_status_enum + AND meta_is_hidden_from_reporting = FALSE + AND historical_grid_id = grid_id + AND created_at >= start_date + AND created_at < end_date; + RETURN total_revenue; +END; +$$; + + +ALTER FUNCTION public.find_energy_topup_revenue(grid_id integer, start_date timestamp with time zone, end_date timestamp with time zone) OWNER TO postgres; + +-- +-- Name: find_top_spenders(integer, integer, timestamp with time zone, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.find_top_spenders(grid_id integer, limit_count integer, start_date timestamp with time zone, end_date timestamp with time zone) RETURNS TABLE(full_name character varying, id integer, amount double precision) + LANGUAGE plpgsql + SET search_path TO '' + AS $$ +BEGIN + RETURN QUERY + SELECT + meta_receiver_name_part_2 AS full_name, + meta_receiver_id_part_2 AS id, + SUM( + _orders.amount +) AS amount + FROM + public.orders AS _orders + WHERE + meta_order_type = 'ENERGY_TOPUP'::public.order_type_enum + AND order_status = 'COMPLETED'::public.order_status_enum + AND meta_is_hidden_from_reporting = FALSE + AND historical_grid_id = grid_id + AND created_at >= start_date + AND created_at < end_date + GROUP BY + meta_receiver_id, + meta_receiver_id_part_2, + meta_receiver_name_part_2 + ORDER BY + SUM( + _orders.amount +) DESC + LIMIT limit_count; +END; +$$; + + +ALTER FUNCTION public.find_top_spenders(grid_id integer, limit_count integer, start_date timestamp with time zone, end_date timestamp with time zone) OWNER TO postgres; + +-- +-- Name: get_grid_status(integer); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.get_grid_status(grid_id integer) RETURNS TABLE(is_hps_on boolean, is_fs_on boolean, are_all_dcus_online boolean, are_all_dcus_under_high_load_threshold boolean, is_cabin_meter_credit_depleting boolean, customer_count integer) + LANGUAGE plpgsql + SET search_path TO '' + AS $$BEGIN + RETURN QUERY + SELECT + _grid.is_hps_on, + _grid.is_fs_on, + _grid.are_all_dcus_online, + _grid.are_all_dcus_under_high_load_threshold, + _grid.is_cabin_meter_credit_depleting, + ( + SELECT + COUNT(DISTINCT _customer.id)::integer + FROM + public.customers _customer + JOIN public.accounts _account ON _account.id = _customer.account_id + JOIN public.connections _connection ON _connection.customer_id = _customer.id + JOIN public.meters _meter ON _meter.connection_id = _connection.id + WHERE + _customer.grid_id = _grid.id + AND _customer.is_hidden_from_reporting = FALSE + AND _account.deleted_at IS NULL) AS customer_count + FROM + public.grids _grid + WHERE + _grid.id = grid_id; +END;$$; + + +ALTER FUNCTION public.get_grid_status(grid_id integer) OWNER TO postgres; + +-- +-- Name: handle_new_user(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.handle_new_user() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + -- RAISE LOG 'New user: %', NEW; + -- Every Supabase User gets an Account that holds more metadata + -- We don't have access to raw_app_meta_data here but we do in + -- the subsequent update trigger + INSERT INTO public.accounts (supabase_id, email, phone, full_name, telegram_link_token) + VALUES(NEW.id, NEW.email, NEW.phone, NEW.raw_user_meta_data ->> 'full_name', gen_random_uuid ()); + RETURN NEW; +END; +$$; + + +ALTER FUNCTION public.handle_new_user() OWNER TO postgres; + +-- +-- Name: handle_update_user(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.handle_update_user() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + UPDATE + public.accounts + SET + email = NEW.email, + phone = NEW.phone, + full_name = NEW.raw_user_meta_data ->> 'full_name', + organization_id = CAST(COALESCE(NEW.raw_app_meta_data ->> 'organization_id', NULL) AS integer) + WHERE + supabase_id = NEW.id; + RETURN NEW; +END; +$$; + + +ALTER FUNCTION public.handle_update_user() OWNER TO postgres; + +-- +-- Name: lock_next_order(uuid); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.lock_next_order(uuid uuid) RETURNS void + LANGUAGE plpgsql + SET search_path TO '' + AS $$ +DECLARE + order_id_var integer; + sender_wallet_id_var integer; + receiver_wallet_id_var integer; +BEGIN + SELECT + _orders.id, + _orders.sender_wallet_id, + _orders.receiver_wallet_id INTO order_id_var, + sender_wallet_id_var, + receiver_wallet_id_var + FROM + public.orders _orders + LEFT JOIN public.wallets AS sender_wallets ON sender_wallets.id = _orders.sender_wallet_id + LEFT JOIN public.wallets AS receiver_wallets ON receiver_wallets.id = _orders.receiver_wallet_id +WHERE + _orders.order_status = 'PENDING' + AND sender_wallets.lock_session IS NULL + AND receiver_wallets.lock_session IS NULL +ORDER BY + _orders.id ASC +LIMIT 1; + UPDATE + public.orders + SET + lock_session = uuid + WHERE + id = order_id_var; + UPDATE + public.wallets + SET + lock_session = uuid + WHERE + id = sender_wallet_id_var + OR id = receiver_wallet_id_var; +END; +$$; + + +ALTER FUNCTION public.lock_next_order(uuid uuid) OWNER TO postgres; + +-- +-- Name: lock_next_order_and_wallets(uuid); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.lock_next_order_and_wallets(uuid uuid) RETURNS TABLE(id integer) + LANGUAGE plpgsql + AS $$ +DECLARE + order_id_var integer; + sender_wallet_id_var integer; + receiver_wallet_id_var integer; +BEGIN + -- 1. Atomic Selection + SELECT + _orders.id, + _orders.sender_wallet_id, + _orders.receiver_wallet_id + INTO + order_id_var, + sender_wallet_id_var, + receiver_wallet_id_var + FROM + public.orders _orders + JOIN public.wallets AS sender_wallets ON sender_wallets.id = _orders.sender_wallet_id + JOIN public.wallets AS receiver_wallets ON receiver_wallets.id = _orders.receiver_wallet_id + WHERE + _orders.order_status = 'PENDING' + AND sender_wallets.lock_session IS NULL + AND receiver_wallets.lock_session IS NULL + ORDER BY + _orders.id ASC + LIMIT 1 + FOR UPDATE OF _orders, sender_wallets, receiver_wallets SKIP LOCKED; + + -- 2. Early exit if no order matches + IF order_id_var IS NULL THEN + RETURN; + END IF; + + -- 3. Update the Order + -- explicit alias 'order_record' prevents confusion with any variables + UPDATE + public.orders AS order_record + SET + lock_session = uuid + WHERE + order_record.id = order_id_var; + + -- 4. Update the Wallets and Return their IDs + -- explicit alias 'wallet_record' prevents confusion + RETURN QUERY + UPDATE + public.wallets AS wallet_record + SET + lock_session = uuid + WHERE + wallet_record.id IN (sender_wallet_id_var, receiver_wallet_id_var) + RETURNING + wallet_record.id; +END; +$$; + + +ALTER FUNCTION public.lock_next_order_and_wallets(uuid uuid) OWNER TO postgres; + +-- +-- Name: lock_next_pd_action(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.lock_next_pd_action() RETURNS integer + LANGUAGE plpgsql + AS $$ +DECLARE + result integer; +BEGIN + SELECT + pd_actions.id INTO result + FROM + pd_actions + WHERE + pd_action_status = 'INITIALISING' + LIMIT 1; + UPDATE + pd_actions + SET + pd_action_status = 'INITIALISED' + WHERE + pd_actions.id = result; + RETURN result; +END; +$$; + + +ALTER FUNCTION public.lock_next_pd_action() OWNER TO postgres; + +-- +-- Name: notify_make_about_is_fs_on_updated(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.notify_make_about_is_fs_on_updated() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$BEGIN + IF NEW.is_fs_on IS DISTINCT FROM OLD.is_fs_on and NEW.is_fs_on_notification_enabled is true THEN + perform net.http_post( + url:='https://hook.eu2.make.com/86h2to0kcomzk49en458ox2pkgm48rv2', + headers := '{"Content-Type": "application/json"}', + body:= json_build_object( + 'grid_id', NEW.id, + 'grid_name', NEW.name, + 'is_fs_on', NEW.is_fs_on, + 'updated_at', now() + )::jsonb + ); + END IF; + RETURN NEW; +END;$$; + + +ALTER FUNCTION public.notify_make_about_is_fs_on_updated() OWNER TO postgres; + +-- +-- Name: notify_make_about_is_hps_on_updated(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.notify_make_about_is_hps_on_updated() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$BEGIN + IF NEW.is_hps_on IS DISTINCT FROM OLD.is_hps_on and NEW.is_energised_notification_enabled is true THEN + perform net.http_post( + url:='https://hook.eu2.make.com/86h2to0kcomzk49en458ox2pkgm48rv2', + headers := '{"Content-Type": "application/json"}', + body:= json_build_object( + 'grid_id', NEW.id, + 'grid_name', NEW.name, + 'is_hps_on', NEW.is_hps_on, + 'updated_at', now() + )::jsonb + ); + END IF; + RETURN NEW; +END;$$; + + +ALTER FUNCTION public.notify_make_about_is_hps_on_updated() OWNER TO postgres; + +-- +-- Name: notify_make_about_kwh_tariff_essential_service_updated(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.notify_make_about_kwh_tariff_essential_service_updated() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$BEGIN + IF NEW.kwh_tariff_essential_service IS DISTINCT FROM OLD.kwh_tariff_essential_service and NEW.is_tariff_change_notification_enabled is true THEN + perform net.http_post( + url:='https://hook.eu2.make.com/86h2to0kcomzk49en458ox2pkgm48rv2', + headers := '{"Content-Type": "application/json"}', + body:= json_build_object( + 'grid_id', NEW.id, + 'grid_name', NEW.name, + 'kwh_tariff_essential_service', NEW.kwh_tariff_essential_service, + 'updated_at', now() + )::jsonb + ); + END IF; + RETURN NEW; +END;$$; + + +ALTER FUNCTION public.notify_make_about_kwh_tariff_essential_service_updated() OWNER TO postgres; + +-- +-- Name: rls_check_if_lender(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.rls_check_if_lender() RETURNS boolean + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + RETURN (auth.jwt () -> 'app_metadata' ->> 'organization_type')::text = 'LENDER'; +END; +$$; + + +ALTER FUNCTION public.rls_check_if_lender() OWNER TO postgres; + +-- +-- Name: rls_check_if_nxt_member(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.rls_check_if_nxt_member() RETURNS boolean + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +DECLARE + nxt_org_id int := 2; + app_meta jsonb := auth.jwt () -> 'app_metadata'; +BEGIN + RETURN (app_meta ->> 'account_type' = 'MEMBER' + AND(app_meta ->> 'organization_id')::int = nxt_org_id); +END; +$$; + + +ALTER FUNCTION public.rls_check_if_nxt_member() OWNER TO postgres; + +-- +-- Name: rls_get_member_org_id(); Type: FUNCTION; Schema: public; Owner: postgres +-- + +CREATE FUNCTION public.rls_get_member_org_id() RETURNS integer + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + RETURN (auth.jwt () -> 'app_metadata' ->> 'organization_id')::int; +END; +$$; + + +ALTER FUNCTION public.rls_get_member_org_id() OWNER TO postgres; + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- Name: accounts; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.accounts ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + full_name character varying, + email character varying, + phone character varying, + telegram_id character varying, + telegram_link_token character varying, + deleted_at timestamp(3) without time zone, + supabase_id uuid, + organization_id integer +); + + +ALTER TABLE public.accounts OWNER TO postgres; + +-- +-- Name: accounts_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.accounts_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.accounts_id_seq OWNER TO postgres; + +-- +-- Name: accounts_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.accounts_id_seq OWNED BY public.accounts.id; + + +-- +-- Name: agents; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.agents ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + grid_id integer, + account_id integer, + rls_organization_id integer +); + + +ALTER TABLE public.agents OWNER TO postgres; + +-- +-- Name: agents_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.agents_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.agents_id_seq OWNER TO postgres; + +-- +-- Name: agents_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.agents_id_seq OWNED BY public.agents.id; + + +-- +-- Name: agents_with_account; Type: VIEW; Schema: public; Owner: postgres +-- + +CREATE VIEW public.agents_with_account WITH (security_invoker='true') AS + SELECT agents.id, + agents.created_at, + agents.grid_id, + agents.account_id, + accounts.full_name, + accounts.phone, + accounts.deleted_at + FROM (public.agents + JOIN public.accounts ON ((agents.account_id = accounts.id))); + + +ALTER VIEW public.agents_with_account OWNER TO postgres; + +-- +-- Name: api_keys; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.api_keys ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + is_locked boolean DEFAULT false NOT NULL, + key character varying, + account_id integer +); + + +ALTER TABLE public.api_keys OWNER TO postgres; + +-- +-- Name: api_keys_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.api_keys_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.api_keys_id_seq OWNER TO postgres; + +-- +-- Name: api_keys_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.api_keys_id_seq OWNED BY public.api_keys.id; + + +-- +-- Name: audits; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.audits ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + message character varying NOT NULL, + from_fs_command boolean DEFAULT false NOT NULL, + author_id integer, + grid_id integer, + organization_id integer, + meter_id integer, + agent_id integer, + customer_id integer, + member_id integer, + connection_id integer, + dcu_id integer +); + + +ALTER TABLE public.audits OWNER TO postgres; + +-- +-- Name: audits_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.audits_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.audits_id_seq OWNER TO postgres; + +-- +-- Name: audits_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.audits_id_seq OWNED BY public.audits.id; + + +-- +-- Name: autopilot_executions; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.autopilot_executions ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + input jsonb, + code text, + output jsonb, + queries jsonb +); + + +ALTER TABLE public.autopilot_executions OWNER TO postgres; + +-- +-- Name: autopilot_executions_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +ALTER TABLE public.autopilot_executions ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.autopilot_executions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: bank_accounts; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.bank_accounts ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + organization_id integer, + bank_id integer +); + + +ALTER TABLE public.bank_accounts OWNER TO postgres; + +-- +-- Name: bank_accounts_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.bank_accounts_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.bank_accounts_id_seq OWNER TO postgres; + +-- +-- Name: bank_accounts_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.bank_accounts_id_seq OWNED BY public.bank_accounts.id; + + +-- +-- Name: banks; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.banks ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + name character varying NOT NULL, + external_id character varying NOT NULL +); + + +ALTER TABLE public.banks OWNER TO postgres; + +-- +-- Name: banks_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.banks_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.banks_id_seq OWNER TO postgres; + +-- +-- Name: banks_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.banks_id_seq OWNED BY public.banks.id; + + +-- +-- Name: dcus; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.dcus ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + external_reference character varying NOT NULL, + queue_buffer_length integer DEFAULT 50 NOT NULL, + external_system public.external_system_enum NOT NULL, + is_online boolean DEFAULT false NOT NULL, + last_online_at timestamp(3) with time zone, + is_online_updated_at timestamp(3) with time zone, + communication_protocol public.communication_protocol_enum, + grid_id integer, + last_metering_hardware_install_session_id integer, + rls_organization_id integer, + location_geom extensions.geometry(Point,4326) +); + + +ALTER TABLE public.dcus OWNER TO postgres; + +-- +-- Name: directives; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.directives ( + id bigint NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + updated_at timestamp(3) with time zone, + directive_priority integer DEFAULT 0 NOT NULL, + directive_status public.directive_status_enum DEFAULT 'PENDING'::public.directive_status_enum NOT NULL, + directive_status_a public.directive_status_enum, + directive_status_b public.directive_status_enum, + directive_status_c public.directive_status_enum, + directive_type public.directive_type_enum NOT NULL, + external_reference character varying, + external_reference_a character varying, + external_reference_b character varying, + external_reference_c character varying, + power_down_count integer, + power_limit integer, + power_limit_should_be integer, + is_on boolean, + kwh double precision, + kwh_credit_available double precision, + voltage double precision, + voltage_a double precision, + voltage_b double precision, + voltage_c double precision, + power double precision, + power_a double precision, + power_b double precision, + power_c double precision, + directive_special_status public.directive_special_status_enum, + directive_error public.directive_error_enum, + token character varying, + meter_version character varying, + execution_session character varying, + status_last_checked_at timestamp(3) with time zone, + status_check_lock_session character varying, + directive_batch_deprecated_id integer, + can_be_retried boolean, + meter_id integer, + directive_batch_execution_id integer, + order_id integer, + meter_commissioning_id integer, + directive_watchdog_session_id integer, + meter_credit_transfer_id integer, + retry_of_directive_id bigint, + author_id integer, + rls_organization_id integer +); + + +ALTER TABLE public.directives OWNER TO postgres; + +-- +-- Name: lorawan_directives; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.lorawan_directives ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + meter_id integer, + author_id integer, + directive_status public.directive_status_enum NOT NULL, + directive_type public.directive_type_enum, + order_id integer, + kwh real, + power_a real, + power_b real, + power_c real, + voltage_a real, + voltage_b real, + voltage_c real, + token text, + is_on boolean, + kwh_credit_available double precision, + current_a double precision, + current_b double precision, + current_c double precision, + can_be_retried boolean, + directive_watchdog_session_id integer, + power_limit integer, + power_limit_should_be integer, + retry_of_directive_id bigint, + meter_commissioning_id integer, + directive_batch_execution_id integer, + value jsonb, + external_reference text, + directive_direction public.directive_direction_enum NOT NULL, + directive_phase public.directive_phase_enum, + rls_organization_id integer, + directive_error public.directive_error_enum +); + + +ALTER TABLE public.lorawan_directives OWNER TO postgres; + +-- +-- Name: meters; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.meters ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + external_reference character varying NOT NULL, + deleted_at timestamp(3) without time zone, + balance double precision, + balance_updated_at timestamp(3) with time zone, + kwh_credit_available double precision, + kwh_credit_available_updated_at timestamp(3) with time zone, + last_non_zero_consumption_at timestamp(3) with time zone, + is_on boolean DEFAULT false, + should_be_on boolean DEFAULT false, + is_on_updated_at timestamp(3) with time zone, + should_be_on_updated_at timestamp(3) with time zone, + is_manual_mode_on boolean DEFAULT false NOT NULL, + is_manual_mode_on_updated_at timestamp(3) with time zone, + voltage double precision, + voltage_updated_at timestamp(3) with time zone, + power double precision, + power_updated_at timestamp(3) with time zone, + latitude double precision, + longitude double precision, + coord_accuracy double precision DEFAULT '0'::double precision NOT NULL, + power_limit integer, + power_limit_updated_at timestamp(3) with time zone, + power_limit_should_be integer, + power_down_count integer, + power_down_count_updated_at timestamp(3) without time zone, + power_limit_should_be_updated_at timestamp(3) with time zone, + is_starred boolean DEFAULT false NOT NULL, + external_system public.external_system_enum NOT NULL, + meter_type public.meter_type_enum DEFAULT 'HPS'::public.meter_type_enum NOT NULL, + nickname character varying, + last_seen_at timestamp(3) with time zone, + issue_check_execution_session character varying, + issue_check_last_run_at timestamp(3) with time zone, + pole_id integer, + meter_phase public.meter_phase_enum DEFAULT 'SINGLE_PHASE'::public.meter_phase_enum NOT NULL, + last_metering_hardware_install_session_id integer, + kwh_tariff double precision, + watchdog_session character varying, + watchdog_last_run_at timestamp(3) with time zone, + version character varying, + is_simulated boolean DEFAULT false NOT NULL, + power_limit_hps_mode integer DEFAULT 200 NOT NULL, + current_special_status public.directive_special_status_enum, + communication_protocol public.communication_protocol_enum DEFAULT 'CALIN_LORAWAN'::public.communication_protocol_enum, + is_cabin_meter boolean DEFAULT false NOT NULL, + last_encountered_issue_id integer, + connection_id integer, + dcu_id integer, + decoder_key text, + last_sts_token_issued_at timestamp with time zone, + rls_grid_id integer, + rls_organization_id integer, + is_test_mode_on boolean DEFAULT false NOT NULL, + pulse_counter_kwh double precision, + device_id bigint, + pulse_counter_kwh_updated_at timestamp with time zone, + connection_metrics jsonb +); + + +ALTER TABLE public.meters OWNER TO postgres; + +-- +-- Name: poles; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.poles ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + external_reference character varying(10) NOT NULL, + nickname character varying, + is_virtual boolean DEFAULT false NOT NULL, + location_accuracy double precision, + grid_id integer, + location_geom extensions.geometry(Point,4326) NOT NULL, + rls_organization_id integer +); + + +ALTER TABLE public.poles OWNER TO postgres; + +-- +-- Name: batch_commands; Type: VIEW; Schema: public; Owner: postgres +-- + +CREATE VIEW public.batch_commands WITH (security_invoker='true') AS + SELECT directives.id, + directives.directive_type, + directives.directive_status, + directives.created_at, + directives.meter_id, + directives.directive_batch_execution_id, + directives.directive_error, + meters.external_reference AS meter_external_reference, + meters.meter_type, + meters.latitude, + meters.longitude, + poles.location_geom, + dcus.id AS dcu_id, + dcus.external_reference AS dcu_external_reference + FROM (((public.directives + LEFT JOIN public.meters ON ((meters.id = directives.meter_id))) + LEFT JOIN public.dcus ON ((dcus.id = meters.dcu_id))) + LEFT JOIN public.poles ON ((poles.id = meters.pole_id))) +UNION ALL + SELECT lorawan_directives.id, + lorawan_directives.directive_type, + lorawan_directives.directive_status, + lorawan_directives.created_at, + lorawan_directives.meter_id, + lorawan_directives.directive_batch_execution_id, + lorawan_directives.directive_error, + meters.external_reference AS meter_external_reference, + meters.meter_type, + meters.latitude, + meters.longitude, + poles.location_geom, + dcus.id AS dcu_id, + dcus.external_reference AS dcu_external_reference + FROM (((public.lorawan_directives + LEFT JOIN public.meters ON ((meters.id = lorawan_directives.meter_id))) + LEFT JOIN public.dcus ON ((dcus.id = meters.dcu_id))) + LEFT JOIN public.poles ON ((poles.id = meters.pole_id))); + + +ALTER VIEW public.batch_commands OWNER TO postgres; + +-- +-- Name: connection_requested_meters; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.connection_requested_meters ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + meter_type public.meter_type_enum NOT NULL, + meter_phase public.meter_phase_enum NOT NULL, + fee double precision DEFAULT '0'::double precision NOT NULL, + deleted_at timestamp(3) without time zone, + connection_id integer NOT NULL, + rls_organization_id integer +); + + +ALTER TABLE public.connection_requested_meters OWNER TO postgres; + +-- +-- Name: connection_requested_meters_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.connection_requested_meters_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.connection_requested_meters_id_seq OWNER TO postgres; + +-- +-- Name: connection_requested_meters_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.connection_requested_meters_id_seq OWNED BY public.connection_requested_meters.id; + + +-- +-- Name: connections; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.connections ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + deleted_at timestamp(3) without time zone, + document_type public.id_document_type_enum DEFAULT 'PASSPORT'::public.id_document_type_enum, + document_id character varying, + upload_uuid character varying, + external_system public.external_system_enum DEFAULT 'EPICOLLECT'::public.external_system_enum, + paid double precision DEFAULT '0'::double precision NOT NULL, + currency public.currency_enum NOT NULL, + women_impacted integer NOT NULL, + is_lifeline boolean, + is_public boolean NOT NULL, + is_commercial boolean NOT NULL, + is_residential boolean NOT NULL, + is_building_wired boolean DEFAULT false, + is_using_led_bulbs boolean DEFAULT false, + customer_id integer, + rls_organization_id integer +); + + +ALTER TABLE public.connections OWNER TO postgres; + +-- +-- Name: connections_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.connections_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.connections_id_seq OWNER TO postgres; + +-- +-- Name: connections_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.connections_id_seq OWNED BY public.connections.id; + + +-- +-- Name: customers; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.customers ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + gender public.gender_enum, + total_connection_fee double precision DEFAULT '0'::double precision NOT NULL, + total_connection_paid double precision DEFAULT '0'::double precision NOT NULL, + is_hidden_from_reporting boolean DEFAULT false NOT NULL, + lives_primarily_in_the_community boolean DEFAULT true NOT NULL, + latitude double precision, + longitude double precision, + generator_owned public.generator_type_enum, + grid_id integer, + account_id integer, + rls_organization_id integer +); + + +ALTER TABLE public.customers OWNER TO postgres; + +-- +-- Name: customers_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.customers_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.customers_id_seq OWNER TO postgres; + +-- +-- Name: customers_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.customers_id_seq OWNED BY public.customers.id; + + +-- +-- Name: customers_with_account; Type: VIEW; Schema: public; Owner: postgres +-- + +CREATE VIEW public.customers_with_account WITH (security_invoker='true') AS + SELECT c.id, + c.created_at, + c.gender, + c.total_connection_fee, + c.total_connection_paid, + c.is_hidden_from_reporting, + c.lives_primarily_in_the_community, + c.latitude, + c.longitude, + c.generator_owned, + c.grid_id, + c.account_id, + a.full_name, + a.phone, + a.deleted_at, + (c.total_connection_paid >= c.total_connection_fee) AS has_fully_paid_connection_fees, + ( SELECT string_agg((m.external_reference)::text, ', '::text) AS string_agg + FROM (public.connections conn + JOIN public.meters m ON ((m.connection_id = conn.id))) + WHERE (conn.customer_id = c.id)) AS meter + FROM (public.customers c + JOIN public.accounts a ON ((c.account_id = a.id))); + + +ALTER VIEW public.customers_with_account OWNER TO postgres; + +-- +-- Name: dcus_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.dcus_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.dcus_id_seq OWNER TO postgres; + +-- +-- Name: dcus_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.dcus_id_seq OWNED BY public.dcus.id; + + +-- +-- Name: device_logs; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.device_logs ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + message text, + device_id bigint, + rls_organization_id integer +); + + +ALTER TABLE public.device_logs OWNER TO postgres; + +-- +-- Name: device_logs_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +ALTER TABLE public.device_logs ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.device_logs_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: device_types; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.device_types ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + name text, + external_system public.external_system_enum +); + + +ALTER TABLE public.device_types OWNER TO postgres; + +-- +-- Name: device_types_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +ALTER TABLE public.device_types ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.device_types_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: devices; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.devices ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + device_type_id bigint NOT NULL, + external_reference text NOT NULL, + grid_id integer, + nickname text, + rls_organization_id integer, + "values" jsonb DEFAULT '{}'::jsonb NOT NULL +); + + +ALTER TABLE public.devices OWNER TO postgres; + +-- +-- Name: devices_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +ALTER TABLE public.devices ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.devices_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: directive_batch_executions; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.directive_batch_executions ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + pending_count integer DEFAULT 0 NOT NULL, + processing_count integer DEFAULT 0 NOT NULL, + successful_count integer DEFAULT 0 NOT NULL, + failed_count integer DEFAULT 0 NOT NULL, + processed_count integer DEFAULT 0 NOT NULL, + total_count integer DEFAULT 0 NOT NULL, + directive_batch_id integer NOT NULL, + rls_organization_id integer, + completed_at timestamp with time zone, + qualified_at timestamp with time zone +); + + +ALTER TABLE public.directive_batch_executions OWNER TO postgres; + +-- +-- Name: directive_batch_executions_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.directive_batch_executions_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.directive_batch_executions_id_seq OWNER TO postgres; + +-- +-- Name: directive_batch_executions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.directive_batch_executions_id_seq OWNED BY public.directive_batch_executions.id; + + +-- +-- Name: directive_batches; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.directive_batches ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + is_deleted boolean DEFAULT false NOT NULL, + hour integer NOT NULL, + minute integer NOT NULL, + is_repeating boolean DEFAULT false NOT NULL, + grid_id integer, + directive_type public.directive_type_enum, + fs_command public.fs_command_type_enum, + lock_session character varying, + execution_bucket integer, + author_id integer, + updated_at timestamp(3) with time zone, + rls_organization_id integer, + task_type public.meter_interaction_type_enum +); + + +ALTER TABLE public.directive_batches OWNER TO postgres; + +-- +-- Name: directive_batches_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.directive_batches_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.directive_batches_id_seq OWNER TO postgres; + +-- +-- Name: directive_batches_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.directive_batches_id_seq OWNED BY public.directive_batches.id; + + +-- +-- Name: directive_watchdog_sessions; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.directive_watchdog_sessions ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + identifier character varying +); + + +ALTER TABLE public.directive_watchdog_sessions OWNER TO postgres; + +-- +-- Name: directive_watchdog_sessions_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.directive_watchdog_sessions_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.directive_watchdog_sessions_id_seq OWNER TO postgres; + +-- +-- Name: directive_watchdog_sessions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.directive_watchdog_sessions_id_seq OWNED BY public.directive_watchdog_sessions.id; + + +-- +-- Name: directives_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.directives_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.directives_id_seq OWNER TO postgres; + +-- +-- Name: directives_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.directives_id_seq OWNED BY public.directives.id; + + +-- +-- Name: energy_cabins; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.energy_cabins ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + grid_id integer NOT NULL, + location_geom extensions.geometry(Point,4326) NOT NULL, + rls_organization_id integer +); + + +ALTER TABLE public.energy_cabins OWNER TO postgres; + +-- +-- Name: energy_cabins_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.energy_cabins_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.energy_cabins_id_seq OWNER TO postgres; + +-- +-- Name: energy_cabins_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.energy_cabins_id_seq OWNED BY public.energy_cabins.id; + + +-- +-- Name: features; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.features ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + uuid character varying NOT NULL, + is_deleted boolean DEFAULT false NOT NULL +); + + +ALTER TABLE public.features OWNER TO postgres; + +-- +-- Name: features_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.features_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.features_id_seq OWNER TO postgres; + +-- +-- Name: features_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.features_id_seq OWNED BY public.features.id; + + +-- +-- Name: grids; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.grids ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + deployed_at timestamp(3) with time zone, + commissioned_at timestamp(3) with time zone, + name character varying NOT NULL, + is_hps_on boolean DEFAULT false NOT NULL, + is_hps_on_updated_at timestamp(3) with time zone, + walkthrough_external_id character varying, + timezone character varying DEFAULT 'Africa/Lagos'::character varying NOT NULL, + kwp double precision DEFAULT '0'::double precision NOT NULL, + kwh double precision DEFAULT '0'::double precision NOT NULL, + kwp_tariff double precision DEFAULT '0'::double precision NOT NULL, + kwh_tariff double precision DEFAULT '0'::double precision NOT NULL, + kwh_tariff_essential_service double precision DEFAULT '0'::double precision NOT NULL, + kwh_tariff_full_service double precision DEFAULT '0'::double precision NOT NULL, + current_weather public.weather_type_enum, + generation_external_system public.external_system_enum DEFAULT 'VICTRON'::public.external_system_enum NOT NULL, + metering_external_system public.external_system_enum DEFAULT 'CALIN'::public.external_system_enum NOT NULL, + generation_external_site_id character varying, + generation_external_gateway_id character varying, + generation_gateway_last_seen_at timestamp(3) with time zone, + is_fs_on boolean DEFAULT false NOT NULL, + is_fs_on_updated_at timestamp(3) with time zone, + should_fs_be_on boolean DEFAULT false NOT NULL, + should_fs_be_on_updated_at timestamp(3) with time zone, + default_hps_connection_fee double precision DEFAULT '0'::double precision NOT NULL, + default_fs_1_phase_connection_fee double precision DEFAULT '0'::double precision NOT NULL, + default_fs_3_phase_connection_fee double precision DEFAULT '0'::double precision NOT NULL, + monthly_rental double precision DEFAULT '0'::double precision NOT NULL, + are_all_dcus_online boolean DEFAULT false NOT NULL, + are_all_dcus_under_high_load_threshold boolean DEFAULT true NOT NULL, + is_hidden_from_reporting boolean DEFAULT true NOT NULL, + is_three_phase_supported boolean DEFAULT false NOT NULL, + is_using_vsat boolean DEFAULT false NOT NULL, + is_using_mobile_network boolean DEFAULT false NOT NULL, + is_hps_on_threshold_kw double precision DEFAULT 0 NOT NULL, + kwh_per_battery_module double precision, + identifier integer, + lifeline_connection_kwh_threshold integer DEFAULT 5 NOT NULL, + lifeline_connection_days_threshold integer DEFAULT 30 NOT NULL, + meter_consumption_issue_threshold_detection_days integer DEFAULT 30 NOT NULL, + meter_communication_issue_threshold_detection_days integer DEFAULT 7 NOT NULL, + uses_dual_meter_setup boolean DEFAULT false NOT NULL, + is_panel_cleaning_notification_enabled boolean DEFAULT false NOT NULL, + is_energised_notification_enabled boolean DEFAULT false NOT NULL, + is_fs_on_notification_enabled boolean DEFAULT false NOT NULL, + is_metering_hardware_online_notification_enabled boolean DEFAULT false NOT NULL, + is_tariff_change_notification_enabled boolean DEFAULT false NOT NULL, + is_upcoming_fs_control_rule_notification_enabled boolean DEFAULT false NOT NULL, + is_fs_control_rule_change_notification_enabled boolean DEFAULT false NOT NULL, + is_automatic_meter_install_enabled boolean DEFAULT false NOT NULL, + is_automatic_payout_generation_enabled boolean DEFAULT false NOT NULL, + is_automatic_energy_generation_data_sync_enabled boolean DEFAULT false NOT NULL, + is_automatic_meter_energy_consumption_data_sync_enabled boolean DEFAULT false NOT NULL, + is_dcu_connectivity_tracking_enabled boolean DEFAULT false NOT NULL, + is_router_connectivity_tracking_enabled boolean DEFAULT false NOT NULL, + is_cabin_meter_credit_depleting boolean DEFAULT false NOT NULL, + telegram_response_path_token character varying, + telegram_notification_channel_invite_link character varying, + telegram_response_path_autopilot character varying, + internal_telegram_group_chat_id character varying, + internal_telegram_group_thread_id character varying, + organization_id integer NOT NULL, + meter_commissioning_initial_credit_kwh real DEFAULT '0'::real NOT NULL, + is_generation_managed_by_nxt_grid boolean DEFAULT true NOT NULL, + location_geom extensions.geometry(Point,4326), + telegram_config jsonb DEFAULT '{}'::jsonb, + feature_access_config jsonb DEFAULT '{}'::jsonb NOT NULL +); + + +ALTER TABLE public.grids OWNER TO postgres; + +-- +-- Name: grids_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.grids_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.grids_id_seq OWNER TO postgres; + +-- +-- Name: grids_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.grids_id_seq OWNED BY public.grids.id; + + +-- +-- Name: issues; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.issues ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + issue_type public.issue_type_enum NOT NULL, + issue_status public.issue_status_enum DEFAULT 'OPEN'::public.issue_status_enum NOT NULL, + external_system public.external_system_enum DEFAULT 'JIRA'::public.external_system_enum NOT NULL, + snoozed_until timestamp with time zone, + started_at timestamp(3) with time zone, + closed_at timestamp(3) with time zone, + estimated_lost_revenue double precision DEFAULT '0'::double precision NOT NULL, + external_reference character varying, + meter_id integer, + mppt_id integer, + grid_id integer, + rls_organization_id integer +); + + +ALTER TABLE public.issues OWNER TO postgres; + +-- +-- Name: issues_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.issues_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.issues_id_seq OWNER TO postgres; + +-- +-- Name: issues_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.issues_id_seq OWNED BY public.issues.id; + + +-- +-- Name: lorawan_directives_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +ALTER TABLE public.lorawan_directives ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.lorawan_directives_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: member_feature; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.member_feature ( + member_id integer NOT NULL, + feature_id integer NOT NULL +); + + +ALTER TABLE public.member_feature OWNER TO postgres; + +-- +-- Name: members; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.members ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + subscribed_to_telegram_revenue_notifications boolean DEFAULT false NOT NULL, + member_type public.member_type_enum DEFAULT 'DEVELOPER'::public.member_type_enum NOT NULL, + account_id integer, + busy_commissioning_id integer, + training_level smallint DEFAULT '0'::smallint NOT NULL, + rls_organization_id integer, + hidden boolean DEFAULT false NOT NULL +); + + +ALTER TABLE public.members OWNER TO postgres; + +-- +-- Name: members_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.members_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.members_id_seq OWNER TO postgres; + +-- +-- Name: members_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.members_id_seq OWNED BY public.members.id; + + +-- +-- Name: meter_commissionings; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.meter_commissionings ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + meter_commissioning_status public.meter_commissioning_status_enum DEFAULT 'PROCESSING'::public.meter_commissioning_status_enum NOT NULL, + initialised_steps integer, + pending_steps integer, + processing_steps integer, + successful_steps integer, + failed_steps integer, + total_steps integer, + lock_session character varying, + metering_hardware_install_session_id integer, + rls_organization_id integer +); + + +ALTER TABLE public.meter_commissionings OWNER TO postgres; + +-- +-- Name: meter_commissionings_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.meter_commissionings_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.meter_commissionings_id_seq OWNER TO postgres; + +-- +-- Name: meter_commissionings_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.meter_commissionings_id_seq OWNED BY public.meter_commissionings.id; + + +-- +-- Name: meter_credit_transfers; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.meter_credit_transfers ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + lock_session character varying, + amount double precision NOT NULL, + meter_credit_transfer_status public.meter_credit_transfer_status_enum DEFAULT 'PENDING'::public.meter_credit_transfer_status_enum NOT NULL, + sender_meter_set_to_amount double precision, + currency public.currency_enum NOT NULL, + sender_meter_id integer NOT NULL, + receiver_meter_id integer NOT NULL, + author_id integer, + rls_organization_id integer +); + + +ALTER TABLE public.meter_credit_transfers OWNER TO postgres; + +-- +-- Name: meter_credit_transfers_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.meter_credit_transfers_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.meter_credit_transfers_id_seq OWNER TO postgres; + +-- +-- Name: meter_credit_transfers_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.meter_credit_transfers_id_seq OWNED BY public.meter_credit_transfers.id; + + +-- +-- Name: meter_interactions; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.meter_interactions ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + meter_id integer NOT NULL, + token text, + order_id integer, + transactive_kwh real, + target_power_limit integer, + result_value jsonb, + meter_interaction_type public.meter_interaction_type_enum NOT NULL, + meter_interaction_status public.meter_interaction_status_enum DEFAULT 'QUEUED'::public.meter_interaction_status_enum NOT NULL, + batch_execution_id integer, + meter_commissioning_id integer, + delivery_failure_history jsonb, + payload_data jsonb +); + + +ALTER TABLE public.meter_interactions OWNER TO postgres; + +-- +-- Name: meter_interactions_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +ALTER TABLE public.meter_interactions ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.meter_interactions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: metering_hardware_imports; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.metering_hardware_imports ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + metering_hardware_import_operation public.mhi_operation_enum DEFAULT 'ADD'::public.mhi_operation_enum NOT NULL, + metering_hardware_import_status public.mhi_status_enum DEFAULT 'PENDING'::public.mhi_status_enum NOT NULL, + metering_hardware_install_session_id integer NOT NULL, + lock_session character varying, + rls_organization_id integer +); + + +ALTER TABLE public.metering_hardware_imports OWNER TO postgres; + +-- +-- Name: metering_hardware_imports_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.metering_hardware_imports_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.metering_hardware_imports_id_seq OWNER TO postgres; + +-- +-- Name: metering_hardware_imports_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.metering_hardware_imports_id_seq OWNED BY public.metering_hardware_imports.id; + + +-- +-- Name: metering_hardware_install_sessions; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.metering_hardware_install_sessions ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + dcu_id integer, + meter_id integer, + author_id integer, + last_meter_commissioning_id integer, + last_metering_hardware_import_id integer, + rls_organization_id integer +); + + +ALTER TABLE public.metering_hardware_install_sessions OWNER TO postgres; + +-- +-- Name: metering_hardware_install_sessions_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.metering_hardware_install_sessions_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.metering_hardware_install_sessions_id_seq OWNER TO postgres; + +-- +-- Name: metering_hardware_install_sessions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.metering_hardware_install_sessions_id_seq OWNED BY public.metering_hardware_install_sessions.id; + + +-- +-- Name: meters_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.meters_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.meters_id_seq OWNER TO postgres; + +-- +-- Name: meters_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.meters_id_seq OWNED BY public.meters.id; + + +-- +-- Name: meters_with_account_and_statuses; Type: VIEW; Schema: public; Owner: postgres +-- + +CREATE VIEW public.meters_with_account_and_statuses WITH (security_invoker='true') AS + SELECT m.id, + m.created_at, + m.external_reference, + m.deleted_at, + m.balance, + m.balance_updated_at, + m.kwh_credit_available, + m.kwh_credit_available_updated_at, + m.last_non_zero_consumption_at, + m.is_on, + m.should_be_on, + m.is_on_updated_at, + m.should_be_on_updated_at, + m.is_manual_mode_on, + m.is_manual_mode_on_updated_at, + m.voltage, + m.voltage_updated_at, + m.power, + m.power_updated_at, + m.latitude, + m.longitude, + m.coord_accuracy, + m.power_limit, + m.power_limit_updated_at, + m.power_limit_should_be, + m.power_down_count, + m.power_down_count_updated_at, + m.power_limit_should_be_updated_at, + m.is_starred, + m.external_system, + m.meter_type, + m.nickname, + m.last_seen_at, + m.issue_check_execution_session, + m.issue_check_last_run_at, + m.pole_id, + m.meter_phase, + m.last_metering_hardware_install_session_id, + m.kwh_tariff, + m.watchdog_session, + m.watchdog_last_run_at, + m.version, + m.is_simulated, + m.power_limit_hps_mode, + m.current_special_status, + m.communication_protocol, + m.is_cabin_meter, + m.last_encountered_issue_id, + m.connection_id, + m.dcu_id, + m.decoder_key, + m.last_sts_token_issued_at, + m.rls_grid_id, + m.rls_organization_id, + m.is_test_mode_on, + m.pulse_counter_kwh, + m.device_id, + c.grid_id, + c.is_hidden_from_reporting, + a.full_name, + a.phone, + ( + CASE + WHEN (m.last_metering_hardware_install_session_id IS NULL) THEN 'SUCCESSFUL'::text + WHEN ((i.metering_hardware_import_status = 'PENDING'::public.mhi_status_enum) OR (com.meter_commissioning_status = 'PENDING'::public.meter_commissioning_status_enum)) THEN 'PENDING'::text + WHEN ((i.metering_hardware_import_status = 'PROCESSING'::public.mhi_status_enum) OR (com.meter_commissioning_status = 'PROCESSING'::public.meter_commissioning_status_enum)) THEN 'PROCESSING'::text + WHEN (com.meter_commissioning_status = 'SUCCESSFUL'::public.meter_commissioning_status_enum) THEN 'SUCCESSFUL'::text + WHEN ((i.metering_hardware_import_status = 'FAILED'::public.mhi_status_enum) OR (com.meter_commissioning_status = 'FAILED'::public.meter_commissioning_status_enum) OR ((i.metering_hardware_import_status = 'SUCCESSFUL'::public.mhi_status_enum) AND (com.meter_commissioning_status IS NULL))) THEN 'FAILED'::text + ELSE NULL::text + END)::public.mhi_status_enum AS install_status, + CASE + WHEN (iss.issue_status = 'OPEN'::public.issue_status_enum) THEN iss.issue_type + ELSE NULL::public.issue_type_enum + END AS open_issue + FROM (((((((public.meters m + JOIN public.connections conn ON ((m.connection_id = conn.id))) + JOIN public.customers c ON ((conn.customer_id = c.id))) + JOIN public.accounts a ON ((c.account_id = a.id))) + LEFT JOIN public.metering_hardware_install_sessions install_session ON ((m.last_metering_hardware_install_session_id = install_session.id))) + LEFT JOIN public.metering_hardware_imports i ON ((install_session.last_metering_hardware_import_id = i.id))) + LEFT JOIN public.meter_commissionings com ON ((install_session.last_meter_commissioning_id = com.id))) + LEFT JOIN public.issues iss ON ((m.last_encountered_issue_id = iss.id))); + + +ALTER VIEW public.meters_with_account_and_statuses OWNER TO postgres; + +-- +-- Name: migrations; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.migrations ( + id integer NOT NULL, + "timestamp" bigint NOT NULL, + name character varying NOT NULL +); + + +ALTER TABLE public.migrations OWNER TO postgres; + +-- +-- Name: migrations_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.migrations_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.migrations_id_seq OWNER TO postgres; + +-- +-- Name: migrations_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.migrations_id_seq OWNED BY public.migrations.id; + + +-- +-- Name: mppts; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.mppts ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + external_reference character varying NOT NULL, + external_id character varying NOT NULL, + external_system public.external_system_enum DEFAULT 'VICTRON'::public.external_system_enum NOT NULL, + kw double precision, + azimuth double precision, + tilt double precision, + installed_at timestamp(3) with time zone DEFAULT now() NOT NULL, + deleted_at timestamp(3) with time zone, + mppt_type public.mppt_type_enum DEFAULT 'MPPT'::public.mppt_type_enum NOT NULL, + grid_id integer, + rls_organization_id integer +); + + +ALTER TABLE public.mppts OWNER TO postgres; + +-- +-- Name: mppts_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.mppts_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.mppts_id_seq OWNER TO postgres; + +-- +-- Name: mppts_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.mppts_id_seq OWNED BY public.mppts.id; + + +-- +-- Name: notes; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.notes ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + message character varying, + customer_id integer, + connection_id integer, + meter_id integer, + author_id integer, + rls_organization_id integer +); + + +ALTER TABLE public.notes OWNER TO postgres; + +-- +-- Name: notes_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.notes_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.notes_id_seq OWNER TO postgres; + +-- +-- Name: notes_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.notes_id_seq OWNED BY public.notes.id; + + +-- +-- Name: notification_parameters; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.notification_parameters ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + parameters json +); + + +ALTER TABLE public.notification_parameters OWNER TO postgres; + +-- +-- Name: notification_parameters_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.notification_parameters_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.notification_parameters_id_seq OWNER TO postgres; + +-- +-- Name: notification_parameters_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.notification_parameters_id_seq OWNED BY public.notification_parameters.id; + + +-- +-- Name: notifications; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.notifications ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + connector_external_system public.external_system_enum, + carrier_external_system public.external_system_enum, + notification_type public.notification_type_enum NOT NULL, + notification_status public.notification_status_enum NOT NULL, + external_reference character varying, + notification_parameter_id integer, + grid_id integer, + organization_id integer, + account_id integer, + lock_session character varying, + message character varying, + phone character varying, + subject character varying, + email character varying, + chat_id character varying, + thread_id character varying +); + + +ALTER TABLE public.notifications OWNER TO postgres; + +-- +-- Name: notifications_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.notifications_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.notifications_id_seq OWNER TO postgres; + +-- +-- Name: notifications_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.notifications_id_seq OWNED BY public.notifications.id; + + +-- +-- Name: orders; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.orders ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + amount double precision NOT NULL, + order_status public.order_status_enum DEFAULT 'PENDING'::public.order_status_enum NOT NULL, + lock_session character varying, + external_reference character varying, + currency public.currency_enum NOT NULL, + external_system public.external_system_enum, + tariff_type public.meter_type_enum, + tariff double precision DEFAULT '-1'::double precision NOT NULL, + payment_method public.payment_method_enum, + payment_channel public.payment_channel_enum, + author_id integer, + directive_id bigint, + meter_credit_transfer_id integer, + historical_grid_id integer, + meta_author_type public.account_type_enum, + meta_author_name character varying, + meta_author_id integer, + meta_order_type public.order_type_enum, + meta_sender_id integer, + meta_sender_name character varying, + meta_receiver_name character varying, + meta_receiver_name_part_2 character varying, + meta_sender_name_part_2 character varying, + meta_receiver_id integer, + meta_sender_type public.order_actor_type_enum, + meta_receiver_type public.order_actor_type_enum, + meta_is_hidden_from_reporting boolean, + sender_wallet_id integer, + receiver_wallet_id integer, + ussd_session_id integer, + lorawan_directive_id bigint, + meta_receiver_id_part_2 integer, + rls_organization_id integer, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +ALTER TABLE public.orders OWNER TO postgres; + +-- +-- Name: orders_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.orders_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.orders_id_seq OWNER TO postgres; + +-- +-- Name: orders_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.orders_id_seq OWNED BY public.orders.id; + + +-- +-- Name: organizations; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.organizations ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + name character varying NOT NULL, + formal_name character varying, + email character varying, + phone character varying, + address character varying, + epicollect_contract_survey_slug character varying, + epicollect_contract_survey_secret character varying, + epicollect_contract_survey_client_id character varying, + epicollect_contract_last_sync_at timestamp(3) with time zone, + developer_group_telegram_chat_id character varying, + deleted_at timestamp with time zone, + pd_hero_google_drive_folder_id text, + organization_type public.organization_type_enum DEFAULT 'SOLAR_DEVELOPER'::public.organization_type_enum NOT NULL +); + + +ALTER TABLE public.organizations OWNER TO postgres; + +-- +-- Name: organizations_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.organizations_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.organizations_id_seq OWNER TO postgres; + +-- +-- Name: organizations_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.organizations_id_seq OWNED BY public.organizations.id; + + +-- +-- Name: payouts; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.payouts ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + proposed_amount double precision NOT NULL, + approved_amount double precision, + external_reference character varying, + external_system public.external_system_enum, + payout_status public.payout_status_enum NOT NULL, + draft_link character varying, + started_at timestamp(3) with time zone NOT NULL, + ended_at timestamp(3) with time zone NOT NULL, + grid_id integer NOT NULL, + details json, + bank_account_id integer, + approved_by_account_id integer +); + + +ALTER TABLE public.payouts OWNER TO postgres; + +-- +-- Name: payouts_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.payouts_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.payouts_id_seq OWNER TO postgres; + +-- +-- Name: payouts_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.payouts_id_seq OWNED BY public.payouts.id; + + +-- +-- Name: pd_action_templates; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.pd_action_templates ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + name text NOT NULL, + pd_flow_template_id bigint NOT NULL, + pd_action_type public.pd_action_type_enum NOT NULL, + depends_on jsonb DEFAULT '[]'::jsonb, + key text, + pd_document_template_id bigint, + parameters jsonb DEFAULT '{}'::jsonb NOT NULL +); + + +ALTER TABLE public.pd_action_templates OWNER TO postgres; + +-- +-- Name: pd_action_templates_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +ALTER TABLE public.pd_action_templates ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.pd_action_templates_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: pd_actions; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.pd_actions ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + name text NOT NULL, + pd_action_status public.pd_action_status_enum DEFAULT 'GENERATING'::public.pd_action_status_enum NOT NULL, + pd_action_type public.pd_action_type_enum NOT NULL, + depends_on jsonb DEFAULT '[]'::jsonb, + pd_flow_id bigint, + pd_section_id bigint, + latest_pd_document_id bigint, + key text, + parameters jsonb DEFAULT '{}'::jsonb NOT NULL, + pd_action_template_id bigint +); + + +ALTER TABLE public.pd_actions OWNER TO postgres; + +-- +-- Name: pd_actions_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +ALTER TABLE public.pd_actions ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.pd_actions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: pd_audits; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.pd_audits ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + author_id integer, + message text, + pd_action_id bigint +); + + +ALTER TABLE public.pd_audits OWNER TO postgres; + +-- +-- Name: pd_audits_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +ALTER TABLE public.pd_audits ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.pd_audits_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: pd_document_templates; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.pd_document_templates ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + google_drive_template_id text NOT NULL, + title text, + parameters jsonb DEFAULT '{}'::jsonb NOT NULL, + pd_document_type public.pd_document_type_enum +); + + +ALTER TABLE public.pd_document_templates OWNER TO postgres; + +-- +-- Name: pd_document_templates_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +ALTER TABLE public.pd_document_templates ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.pd_document_templates_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: pd_documents; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.pd_documents ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + pd_action_id bigint, + google_drive_document_id text NOT NULL, + title text, + pd_document_type public.pd_document_type_enum +); + + +ALTER TABLE public.pd_documents OWNER TO postgres; + +-- +-- Name: pd_documents_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +ALTER TABLE public.pd_documents ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.pd_documents_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: pd_flow_templates; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.pd_flow_templates ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + name text NOT NULL, + parameters jsonb DEFAULT '{}'::jsonb NOT NULL +); + + +ALTER TABLE public.pd_flow_templates OWNER TO postgres; + +-- +-- Name: pd_flow_templates_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +ALTER TABLE public.pd_flow_templates ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.pd_flow_templates_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: pd_flows; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.pd_flows ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + pd_flow_template_id bigint NOT NULL, + google_folder_id text, + title text, + parameters jsonb DEFAULT '{}'::jsonb NOT NULL +); + + +ALTER TABLE public.pd_flows OWNER TO postgres; + +-- +-- Name: pd_flows_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +ALTER TABLE public.pd_flows ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.pd_flows_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: pd_section_templates; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.pd_section_templates ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + title character varying +); + + +ALTER TABLE public.pd_section_templates OWNER TO postgres; + +-- +-- Name: pd_section_templates_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +ALTER TABLE public.pd_section_templates ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.pd_section_templates_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: pd_sections; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.pd_sections ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + title text +); + + +ALTER TABLE public.pd_sections OWNER TO postgres; + +-- +-- Name: pd_sections_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +ALTER TABLE public.pd_sections ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.pd_sections_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: pd_site_submissions; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.pd_site_submissions ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + author_full_name text NOT NULL, + author_email text NOT NULL, + author_organization_name text NOT NULL, + site_name character varying NOT NULL, + site_details jsonb NOT NULL, + outline_geom extensions.geometry(Polygon,4326), + deleted_at timestamp with time zone, + location_geom extensions.geometry(Point,4326), + organization_id integer, + author_organization_id integer, + buildings_geo_flat jsonb, + distribution_geo_flat jsonb, + meta_geo_flat jsonb, + poles_geo_flat jsonb +); + + +ALTER TABLE public.pd_site_submissions OWNER TO postgres; + +-- +-- Name: pd_site_submissions_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +ALTER TABLE public.pd_site_submissions ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.pd_site_submissions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: pd_sites; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.pd_sites ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + organization_id integer, + name text NOT NULL, + deleted_at timestamp with time zone, + location_geom extensions.geometry(Point,4326), + outline_geom extensions.geometry(Polygon,4326), + pd_flow_id bigint, + operations_grid_id integer +); + + +ALTER TABLE public.pd_sites OWNER TO postgres; + +-- +-- Name: pd_sites_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +ALTER TABLE public.pd_sites ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.pd_sites_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: poles_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.poles_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.poles_id_seq OWNER TO postgres; + +-- +-- Name: poles_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.poles_id_seq OWNED BY public.poles.id; + + +-- +-- Name: routers; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.routers ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + external_reference character varying NOT NULL, + external_system public.external_system_enum DEFAULT 'ZEROTIER'::public.external_system_enum NOT NULL, + is_online boolean DEFAULT false NOT NULL, + is_online_updated_at timestamp(3) with time zone, + deleted_at timestamp(3) with time zone, + grid_id integer, + rls_organization_id integer +); + + +ALTER TABLE public.routers OWNER TO postgres; + +-- +-- Name: routers_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.routers_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.routers_id_seq OWNER TO postgres; + +-- +-- Name: routers_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.routers_id_seq OWNED BY public.routers.id; + + +-- +-- Name: solcast_cache; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.solcast_cache ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + request_type public.solcast_cache_request_type_enum NOT NULL, + latitude numeric(8,6) NOT NULL, + longitude numeric(9,6) NOT NULL, + tilt numeric(10,3) NOT NULL, + azimuth numeric(10,3) NOT NULL, + capacity_kwp numeric(10,3) NOT NULL, + install_date character varying NOT NULL, + response text NOT NULL +); + + +ALTER TABLE public.solcast_cache OWNER TO postgres; + +-- +-- Name: solcast_cache_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.solcast_cache_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.solcast_cache_id_seq OWNER TO postgres; + +-- +-- Name: solcast_cache_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.solcast_cache_id_seq OWNED BY public.solcast_cache.id; + + +-- +-- Name: transactions; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.transactions ( + id bigint NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + amount double precision NOT NULL, + transaction_status public.transaction_status_enum NOT NULL, + balance_before double precision DEFAULT '-1'::double precision NOT NULL, + balance_after double precision DEFAULT '-1'::double precision NOT NULL, + wallet_id integer, + order_id integer, + rls_organization_id integer +); + + +ALTER TABLE public.transactions OWNER TO postgres; + +-- +-- Name: transactions_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.transactions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.transactions_id_seq OWNER TO postgres; + +-- +-- Name: transactions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.transactions_id_seq OWNED BY public.transactions.id; + + +-- +-- Name: ussd_session_hops; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.ussd_session_hops ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + text character varying, + phone character varying, + network_code character varying, + service_code character varying, + ussd_session_id integer +); + + +ALTER TABLE public.ussd_session_hops OWNER TO postgres; + +-- +-- Name: ussd_session_hops_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.ussd_session_hops_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.ussd_session_hops_id_seq OWNER TO postgres; + +-- +-- Name: ussd_session_hops_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.ussd_session_hops_id_seq OWNED BY public.ussd_session_hops.id; + + +-- +-- Name: ussd_sessions; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.ussd_sessions ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + phone character varying NOT NULL, + amount double precision, + external_reference character varying, + external_system public.external_system_enum NOT NULL, + is_using_other_option boolean DEFAULT false NOT NULL, + account_id integer, + meter_id integer, + bank_id integer +); + + +ALTER TABLE public.ussd_sessions OWNER TO postgres; + +-- +-- Name: ussd_sessions_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.ussd_sessions_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.ussd_sessions_id_seq OWNER TO postgres; + +-- +-- Name: ussd_sessions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.ussd_sessions_id_seq OWNED BY public.ussd_sessions.id; + + +-- +-- Name: wallets; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.wallets ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + organization_id integer, + agent_id integer, + customer_id integer, + meter_id integer, + lock_session character varying, + balance double precision DEFAULT '0'::double precision NOT NULL, + balance_updated_at timestamp(3) with time zone, + identifier character varying, + wallet_type public.wallet_type_enum DEFAULT 'REAL'::public.wallet_type_enum NOT NULL, + goldring_migration_id integer, + connection_id integer, + rls_organization_id integer +); + + +ALTER TABLE public.wallets OWNER TO postgres; + +-- +-- Name: wallets_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.wallets_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.wallets_id_seq OWNER TO postgres; + +-- +-- Name: wallets_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.wallets_id_seq OWNED BY public.wallets.id; + + +-- +-- Name: accounts id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.accounts ALTER COLUMN id SET DEFAULT nextval('public.accounts_id_seq'::regclass); + + +-- +-- Name: agents id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.agents ALTER COLUMN id SET DEFAULT nextval('public.agents_id_seq'::regclass); + + +-- +-- Name: api_keys id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.api_keys ALTER COLUMN id SET DEFAULT nextval('public.api_keys_id_seq'::regclass); + + +-- +-- Name: audits id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.audits ALTER COLUMN id SET DEFAULT nextval('public.audits_id_seq'::regclass); + + +-- +-- Name: bank_accounts id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.bank_accounts ALTER COLUMN id SET DEFAULT nextval('public.bank_accounts_id_seq'::regclass); + + +-- +-- Name: banks id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.banks ALTER COLUMN id SET DEFAULT nextval('public.banks_id_seq'::regclass); + + +-- +-- Name: connection_requested_meters id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.connection_requested_meters ALTER COLUMN id SET DEFAULT nextval('public.connection_requested_meters_id_seq'::regclass); + + +-- +-- Name: connections id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.connections ALTER COLUMN id SET DEFAULT nextval('public.connections_id_seq'::regclass); + + +-- +-- Name: customers id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.customers ALTER COLUMN id SET DEFAULT nextval('public.customers_id_seq'::regclass); + + +-- +-- Name: dcus id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.dcus ALTER COLUMN id SET DEFAULT nextval('public.dcus_id_seq'::regclass); + + +-- +-- Name: directive_batch_executions id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.directive_batch_executions ALTER COLUMN id SET DEFAULT nextval('public.directive_batch_executions_id_seq'::regclass); + + +-- +-- Name: directive_batches id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.directive_batches ALTER COLUMN id SET DEFAULT nextval('public.directive_batches_id_seq'::regclass); + + +-- +-- Name: directive_watchdog_sessions id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.directive_watchdog_sessions ALTER COLUMN id SET DEFAULT nextval('public.directive_watchdog_sessions_id_seq'::regclass); + + +-- +-- Name: directives id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.directives ALTER COLUMN id SET DEFAULT nextval('public.directives_id_seq'::regclass); + + +-- +-- Name: energy_cabins id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.energy_cabins ALTER COLUMN id SET DEFAULT nextval('public.energy_cabins_id_seq'::regclass); + + +-- +-- Name: features id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.features ALTER COLUMN id SET DEFAULT nextval('public.features_id_seq'::regclass); + + +-- +-- Name: grids id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.grids ALTER COLUMN id SET DEFAULT nextval('public.grids_id_seq'::regclass); + + +-- +-- Name: issues id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.issues ALTER COLUMN id SET DEFAULT nextval('public.issues_id_seq'::regclass); + + +-- +-- Name: members id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.members ALTER COLUMN id SET DEFAULT nextval('public.members_id_seq'::regclass); + + +-- +-- Name: meter_commissionings id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meter_commissionings ALTER COLUMN id SET DEFAULT nextval('public.meter_commissionings_id_seq'::regclass); + + +-- +-- Name: meter_credit_transfers id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meter_credit_transfers ALTER COLUMN id SET DEFAULT nextval('public.meter_credit_transfers_id_seq'::regclass); + + +-- +-- Name: metering_hardware_imports id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.metering_hardware_imports ALTER COLUMN id SET DEFAULT nextval('public.metering_hardware_imports_id_seq'::regclass); + + +-- +-- Name: metering_hardware_install_sessions id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.metering_hardware_install_sessions ALTER COLUMN id SET DEFAULT nextval('public.metering_hardware_install_sessions_id_seq'::regclass); + + +-- +-- Name: meters id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meters ALTER COLUMN id SET DEFAULT nextval('public.meters_id_seq'::regclass); + + +-- +-- Name: migrations id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.migrations ALTER COLUMN id SET DEFAULT nextval('public.migrations_id_seq'::regclass); + + +-- +-- Name: mppts id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.mppts ALTER COLUMN id SET DEFAULT nextval('public.mppts_id_seq'::regclass); + + +-- +-- Name: notes id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.notes ALTER COLUMN id SET DEFAULT nextval('public.notes_id_seq'::regclass); + + +-- +-- Name: notification_parameters id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.notification_parameters ALTER COLUMN id SET DEFAULT nextval('public.notification_parameters_id_seq'::regclass); + + +-- +-- Name: notifications id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.notifications ALTER COLUMN id SET DEFAULT nextval('public.notifications_id_seq'::regclass); + + +-- +-- Name: orders id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.orders ALTER COLUMN id SET DEFAULT nextval('public.orders_id_seq'::regclass); + + +-- +-- Name: organizations id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.organizations ALTER COLUMN id SET DEFAULT nextval('public.organizations_id_seq'::regclass); + + +-- +-- Name: payouts id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.payouts ALTER COLUMN id SET DEFAULT nextval('public.payouts_id_seq'::regclass); + + +-- +-- Name: poles id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.poles ALTER COLUMN id SET DEFAULT nextval('public.poles_id_seq'::regclass); + + +-- +-- Name: routers id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.routers ALTER COLUMN id SET DEFAULT nextval('public.routers_id_seq'::regclass); + + +-- +-- Name: solcast_cache id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.solcast_cache ALTER COLUMN id SET DEFAULT nextval('public.solcast_cache_id_seq'::regclass); + + +-- +-- Name: transactions id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.transactions ALTER COLUMN id SET DEFAULT nextval('public.transactions_id_seq'::regclass); + + +-- +-- Name: ussd_session_hops id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.ussd_session_hops ALTER COLUMN id SET DEFAULT nextval('public.ussd_session_hops_id_seq'::regclass); + + +-- +-- Name: ussd_sessions id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.ussd_sessions ALTER COLUMN id SET DEFAULT nextval('public.ussd_sessions_id_seq'::regclass); + + +-- +-- Name: wallets id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.wallets ALTER COLUMN id SET DEFAULT nextval('public.wallets_id_seq'::regclass); + + +-- +-- Name: connections PK_0a1f844af3122354cbd487a8d03; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.connections + ADD CONSTRAINT "PK_0a1f844af3122354cbd487a8d03" PRIMARY KEY (id); + + +-- +-- Name: meters PK_0a71b52dbb545fa36efaf070583; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "PK_0a71b52dbb545fa36efaf070583" PRIMARY KEY (id); + + +-- +-- Name: customers PK_133ec679a801fab5e070f73d3ea; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.customers + ADD CONSTRAINT "PK_133ec679a801fab5e070f73d3ea" PRIMARY KEY (id); + + +-- +-- Name: poles PK_1f4336016e8de1d62acb05ce829; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.poles + ADD CONSTRAINT "PK_1f4336016e8de1d62acb05ce829" PRIMARY KEY (id); + + +-- +-- Name: members PK_28b53062261b996d9c99fa12404; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.members + ADD CONSTRAINT "PK_28b53062261b996d9c99fa12404" PRIMARY KEY (id); + + +-- +-- Name: member_feature PK_2e0913be5216d601bf212e58fc9; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.member_feature + ADD CONSTRAINT "PK_2e0913be5216d601bf212e58fc9" PRIMARY KEY (member_id, feature_id); + + +-- +-- Name: notification_parameters PK_338f36e72b69406bed36e054e09; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.notification_parameters + ADD CONSTRAINT "PK_338f36e72b69406bed36e054e09" PRIMARY KEY (id); + + +-- +-- Name: directives PK_33cbab4c60a227ae4053fcaea3c; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.directives + ADD CONSTRAINT "PK_33cbab4c60a227ae4053fcaea3c" PRIMARY KEY (id); + + +-- +-- Name: banks PK_3975b5f684ec241e3901db62d77; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.banks + ADD CONSTRAINT "PK_3975b5f684ec241e3901db62d77" PRIMARY KEY (id); + + +-- +-- Name: metering_hardware_install_sessions PK_4eb998262e5c773a89dfe3ea0e1; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "PK_4eb998262e5c773a89dfe3ea0e1" PRIMARY KEY (id); + + +-- +-- Name: ussd_session_hops PK_56d984bae21ce6e86848a8a3c04; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.ussd_session_hops + ADD CONSTRAINT "PK_56d984bae21ce6e86848a8a3c04" PRIMARY KEY (id); + + +-- +-- Name: energy_cabins PK_59ecb7965974e45fdc2f243bda7; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.energy_cabins + ADD CONSTRAINT "PK_59ecb7965974e45fdc2f243bda7" PRIMARY KEY (id); + + +-- +-- Name: accounts PK_5a7a02c20412299d198e097a8fe; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.accounts + ADD CONSTRAINT "PK_5a7a02c20412299d198e097a8fe" PRIMARY KEY (id); + + +-- +-- Name: features PK_5c1e336df2f4a7051e5bf08a941; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.features + ADD CONSTRAINT "PK_5c1e336df2f4a7051e5bf08a941" PRIMARY KEY (id); + + +-- +-- Name: api_keys PK_5c8a79801b44bd27b79228e1dad; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.api_keys + ADD CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad" PRIMARY KEY (id); + + +-- +-- Name: metering_hardware_imports PK_665b32b395a7675648ed77da4f6; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.metering_hardware_imports + ADD CONSTRAINT "PK_665b32b395a7675648ed77da4f6" PRIMARY KEY (id); + + +-- +-- Name: meter_commissionings PK_69fce91302a5dc8dd3db274717b; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meter_commissionings + ADD CONSTRAINT "PK_69fce91302a5dc8dd3db274717b" PRIMARY KEY (id); + + +-- +-- Name: notifications PK_6a72c3c0f683f6462415e653c3a; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.notifications + ADD CONSTRAINT "PK_6a72c3c0f683f6462415e653c3a" PRIMARY KEY (id); + + +-- +-- Name: organizations PK_6b031fcd0863e3f6b44230163f9; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.organizations + ADD CONSTRAINT "PK_6b031fcd0863e3f6b44230163f9" PRIMARY KEY (id); + + +-- +-- Name: orders PK_710e2d4957aa5878dfe94e4ac2f; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "PK_710e2d4957aa5878dfe94e4ac2f" PRIMARY KEY (id); + + +-- +-- Name: payouts PK_76855dc4f0a6c18c72eea302e87; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.payouts + ADD CONSTRAINT "PK_76855dc4f0a6c18c72eea302e87" PRIMARY KEY (id); + + +-- +-- Name: dcus PK_82192607fa5119101a78fe53b7d; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.dcus + ADD CONSTRAINT "PK_82192607fa5119101a78fe53b7d" PRIMARY KEY (id); + + +-- +-- Name: wallets PK_8402e5df5a30a229380e83e4f7e; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "PK_8402e5df5a30a229380e83e4f7e" PRIMARY KEY (id); + + +-- +-- Name: grids PK_840d40fdcde1e935afd43082aca; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.grids + ADD CONSTRAINT "PK_840d40fdcde1e935afd43082aca" PRIMARY KEY (id); + + +-- +-- Name: meter_credit_transfers PK_8ad697d3bf35e128cf01874b202; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meter_credit_transfers + ADD CONSTRAINT "PK_8ad697d3bf35e128cf01874b202" PRIMARY KEY (id); + + +-- +-- Name: migrations PK_8c82d7f526340ab734260ea46be; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.migrations + ADD CONSTRAINT "PK_8c82d7f526340ab734260ea46be" PRIMARY KEY (id); + + +-- +-- Name: directive_batches PK_92d424811a3fd49a8020eadb97a; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.directive_batches + ADD CONSTRAINT "PK_92d424811a3fd49a8020eadb97a" PRIMARY KEY (id); + + +-- +-- Name: directive_batch_executions PK_9494ab23abdfe4e59f3f29842d5; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.directive_batch_executions + ADD CONSTRAINT "PK_9494ab23abdfe4e59f3f29842d5" PRIMARY KEY (id); + + +-- +-- Name: agents PK_9c653f28ae19c5884d5baf6a1d9; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.agents + ADD CONSTRAINT "PK_9c653f28ae19c5884d5baf6a1d9" PRIMARY KEY (id); + + +-- +-- Name: issues PK_9d8ecbbeff46229c700f0449257; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.issues + ADD CONSTRAINT "PK_9d8ecbbeff46229c700f0449257" PRIMARY KEY (id); + + +-- +-- Name: solcast_cache PK_a0b1d6bdbc5ca0056201a1c6dd0; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.solcast_cache + ADD CONSTRAINT "PK_a0b1d6bdbc5ca0056201a1c6dd0" PRIMARY KEY (id); + + +-- +-- Name: transactions PK_a219afd8dd77ed80f5a862f1db9; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.transactions + ADD CONSTRAINT "PK_a219afd8dd77ed80f5a862f1db9" PRIMARY KEY (id); + + +-- +-- Name: notes PK_af6206538ea96c4e77e9f400c3d; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.notes + ADD CONSTRAINT "PK_af6206538ea96c4e77e9f400c3d" PRIMARY KEY (id); + + +-- +-- Name: audits PK_b2d7a2089999197dc7024820f28; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "PK_b2d7a2089999197dc7024820f28" PRIMARY KEY (id); + + +-- +-- Name: routers PK_b6d283f1e40d4942dedbc0cb27a; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.routers + ADD CONSTRAINT "PK_b6d283f1e40d4942dedbc0cb27a" PRIMARY KEY (id); + + +-- +-- Name: directive_watchdog_sessions PK_c05de70ec36246cd4a4cfc01249; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.directive_watchdog_sessions + ADD CONSTRAINT "PK_c05de70ec36246cd4a4cfc01249" PRIMARY KEY (id); + + +-- +-- Name: ussd_sessions PK_c18f16f36e79b2fb87783476830; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.ussd_sessions + ADD CONSTRAINT "PK_c18f16f36e79b2fb87783476830" PRIMARY KEY (id); + + +-- +-- Name: bank_accounts PK_c872de764f2038224a013ff25ed; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.bank_accounts + ADD CONSTRAINT "PK_c872de764f2038224a013ff25ed" PRIMARY KEY (id); + + +-- +-- Name: mppts PK_c8a4dc57b932c173f6c579804c0; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.mppts + ADD CONSTRAINT "PK_c8a4dc57b932c173f6c579804c0" PRIMARY KEY (id); + + +-- +-- Name: connection_requested_meters PK_ef90f8d1aa3055757e0ff8e4aa7; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.connection_requested_meters + ADD CONSTRAINT "PK_ef90f8d1aa3055757e0ff8e4aa7" PRIMARY KEY (id); + + +-- +-- Name: orders REL_093ca3525311b8a12f6cf6b1c9; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "REL_093ca3525311b8a12f6cf6b1c9" UNIQUE (directive_id); + + +-- +-- Name: accounts REL_13bf998d703e7d4c7b9a90f4f7; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.accounts + ADD CONSTRAINT "REL_13bf998d703e7d4c7b9a90f4f7" UNIQUE (supabase_id); + + +-- +-- Name: metering_hardware_install_sessions REL_4eb64f5decb7250cfa993410c1; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "REL_4eb64f5decb7250cfa993410c1" UNIQUE (last_meter_commissioning_id); + + +-- +-- Name: meters REL_5606d6ec5ab568377509edc526; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "REL_5606d6ec5ab568377509edc526" UNIQUE (last_encountered_issue_id); + + +-- +-- Name: wallets REL_6580899a2293de27787376887f; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "REL_6580899a2293de27787376887f" UNIQUE (customer_id); + + +-- +-- Name: meters REL_86d4557a79e374b5c55cb3b66d; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "REL_86d4557a79e374b5c55cb3b66d" UNIQUE (last_metering_hardware_install_session_id); + + +-- +-- Name: wallets REL_8db1f4e4f8122bd25d50ad96b2; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "REL_8db1f4e4f8122bd25d50ad96b2" UNIQUE (meter_id); + + +-- +-- Name: orders REL_92081072063eaf51300ab6c267; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "REL_92081072063eaf51300ab6c267" UNIQUE (ussd_session_id); + + +-- +-- Name: dcus REL_9334b587e6e5291f8ccd1b11c4; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.dcus + ADD CONSTRAINT "REL_9334b587e6e5291f8ccd1b11c4" UNIQUE (last_metering_hardware_install_session_id); + + +-- +-- Name: orders REL_a53c58bdb5ae0193f17497c81b; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "REL_a53c58bdb5ae0193f17497c81b" UNIQUE (meter_credit_transfer_id); + + +-- +-- Name: metering_hardware_install_sessions REL_be64a09dc738420a409cb96026; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "REL_be64a09dc738420a409cb96026" UNIQUE (last_metering_hardware_import_id); + + +-- +-- Name: agents REL_df520decc9e003a843d8edd986; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.agents + ADD CONSTRAINT "REL_df520decc9e003a843d8edd986" UNIQUE (account_id); + + +-- +-- Name: wallets REL_e63e504d8e35ef37a2c56b75eb; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "REL_e63e504d8e35ef37a2c56b75eb" UNIQUE (connection_id); + + +-- +-- Name: customers REL_ebcc29963874e55053e8ee80be; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.customers + ADD CONSTRAINT "REL_ebcc29963874e55053e8ee80be" UNIQUE (account_id); + + +-- +-- Name: wallets REL_f499c61c6d6a0ac3f794d966ed; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "REL_f499c61c6d6a0ac3f794d966ed" UNIQUE (organization_id); + + +-- +-- Name: wallets REL_f5782e05e8688f0cfbb5c4a52c; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "REL_f5782e05e8688f0cfbb5c4a52c" UNIQUE (agent_id); + + +-- +-- Name: members REL_fd9dfb97e21b75fc45d42aa614; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.members + ADD CONSTRAINT "REL_fd9dfb97e21b75fc45d42aa614" UNIQUE (account_id); + + +-- +-- Name: accounts UQ_0ec8c228e89a95aeb4af9ee3226; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.accounts + ADD CONSTRAINT "UQ_0ec8c228e89a95aeb4af9ee3226" UNIQUE (telegram_link_token); + + +-- +-- Name: accounts UQ_3ffa9ce30e56b6d2abf0a465f5f; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.accounts + ADD CONSTRAINT "UQ_3ffa9ce30e56b6d2abf0a465f5f" UNIQUE (telegram_id); + + +-- +-- Name: features UQ_49b9cdf3c7de1a95941e7187764; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.features + ADD CONSTRAINT "UQ_49b9cdf3c7de1a95941e7187764" UNIQUE (uuid); + + +-- +-- Name: grids UQ_61f08f04c9f0f1d3afd24b9be0b; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.grids + ADD CONSTRAINT "UQ_61f08f04c9f0f1d3afd24b9be0b" UNIQUE (identifier); + + +-- +-- Name: poles UQ_67112e4334d7090a571d2fff42a; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.poles + ADD CONSTRAINT "UQ_67112e4334d7090a571d2fff42a" UNIQUE (external_reference); + + +-- +-- Name: api_keys UQ_e42cf55faeafdcce01a82d24849; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.api_keys + ADD CONSTRAINT "UQ_e42cf55faeafdcce01a82d24849" UNIQUE (key); + + +-- +-- Name: autopilot_executions autopilot_executions_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.autopilot_executions + ADD CONSTRAINT autopilot_executions_pkey PRIMARY KEY (id); + + +-- +-- Name: device_logs device_logs_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.device_logs + ADD CONSTRAINT device_logs_pkey PRIMARY KEY (id); + + +-- +-- Name: device_types device_types_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.device_types + ADD CONSTRAINT device_types_pkey PRIMARY KEY (id); + + +-- +-- Name: devices devices_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.devices + ADD CONSTRAINT devices_pkey PRIMARY KEY (id); + + +-- +-- Name: lorawan_directives lorawan_directives_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.lorawan_directives + ADD CONSTRAINT lorawan_directives_pkey PRIMARY KEY (id); + + +-- +-- Name: meter_interactions meter_interactions_order_id_key; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meter_interactions + ADD CONSTRAINT meter_interactions_order_id_key UNIQUE (order_id); + + +-- +-- Name: meter_interactions meter_interactions_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meter_interactions + ADD CONSTRAINT meter_interactions_pkey PRIMARY KEY (id); + + +-- +-- Name: meters meters_device_id_key; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT meters_device_id_key UNIQUE (device_id); + + +-- +-- Name: pd_action_templates pd_action_templates_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_action_templates + ADD CONSTRAINT pd_action_templates_pkey PRIMARY KEY (id); + + +-- +-- Name: pd_actions pd_actions_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_actions + ADD CONSTRAINT pd_actions_pkey PRIMARY KEY (id); + + +-- +-- Name: pd_audits pd_audits_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_audits + ADD CONSTRAINT pd_audits_pkey PRIMARY KEY (id); + + +-- +-- Name: pd_document_templates pd_document_templates_google_drive_template_id_key; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_document_templates + ADD CONSTRAINT pd_document_templates_google_drive_template_id_key UNIQUE (google_drive_template_id); + + +-- +-- Name: pd_document_templates pd_document_templates_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_document_templates + ADD CONSTRAINT pd_document_templates_pkey PRIMARY KEY (id); + + +-- +-- Name: pd_documents pd_documents_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_documents + ADD CONSTRAINT pd_documents_pkey PRIMARY KEY (id); + + +-- +-- Name: pd_flow_templates pd_flow_templates_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_flow_templates + ADD CONSTRAINT pd_flow_templates_pkey PRIMARY KEY (id); + + +-- +-- Name: pd_flows pd_flows_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_flows + ADD CONSTRAINT pd_flows_pkey PRIMARY KEY (id); + + +-- +-- Name: pd_site_submissions pd_public_submissions_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_site_submissions + ADD CONSTRAINT pd_public_submissions_pkey PRIMARY KEY (id); + + +-- +-- Name: pd_section_templates pd_section_templates_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_section_templates + ADD CONSTRAINT pd_section_templates_pkey PRIMARY KEY (id); + + +-- +-- Name: pd_sections pd_sections_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_sections + ADD CONSTRAINT pd_sections_pkey PRIMARY KEY (id); + + +-- +-- Name: pd_sites pd_sites_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_sites + ADD CONSTRAINT pd_sites_pkey PRIMARY KEY (id); + + +-- +-- Name: pd_action_templates unique_pd_action_templates_key; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_action_templates + ADD CONSTRAINT unique_pd_action_templates_key UNIQUE (pd_flow_template_id, key); + + +-- +-- Name: pd_actions unique_pd_actions_key; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_actions + ADD CONSTRAINT unique_pd_actions_key UNIQUE (pd_flow_id, key); + + +-- +-- Name: IDX_006cef4b555e8b1069cb7236db; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "IDX_006cef4b555e8b1069cb7236db" ON public.directives USING btree (meter_id, directive_status, created_at); + + +-- +-- Name: IDX_14f6e0badc019bbdd2f66f7e8a; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "IDX_14f6e0badc019bbdd2f66f7e8a" ON public.notifications USING btree (notification_status); + + +-- +-- Name: IDX_354415e311ebb83a9dfa662051; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "IDX_354415e311ebb83a9dfa662051" ON public.directives USING btree (execution_session); + + +-- +-- Name: IDX_36d092cd2d89756f62421011bb; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX "IDX_36d092cd2d89756f62421011bb" ON public.meters USING btree (external_reference, external_system); + + +-- +-- Name: IDX_4c8f70f01a77b717b9659b564d; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "IDX_4c8f70f01a77b717b9659b564d" ON public.directives USING btree (directive_status); + + +-- +-- Name: IDX_4da956d70d1de38087b251b997; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "IDX_4da956d70d1de38087b251b997" ON public.member_feature USING btree (feature_id); + + +-- +-- Name: IDX_5c820aa56824815f8e19484ff5; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "IDX_5c820aa56824815f8e19484ff5" ON public.transactions USING btree (wallet_id, transaction_status, created_at); + + +-- +-- Name: IDX_9c4f5693f52e6e5e51e1ca05af; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE UNIQUE INDEX "IDX_9c4f5693f52e6e5e51e1ca05af" ON public.dcus USING btree (external_reference, external_system); + + +-- +-- Name: IDX_c312f698867204bf4f1fd149c9; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "IDX_c312f698867204bf4f1fd149c9" ON public.member_feature USING btree (member_id); + + +-- +-- Name: IDX_df09009e4a41315a462745d2c8; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX "IDX_df09009e4a41315a462745d2c8" ON public.directives USING btree (token); + + +-- +-- Name: idx_agents_rls_organization_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_agents_rls_organization_id ON public.agents USING btree (rls_organization_id); + + +-- +-- Name: idx_connection_customer_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_connection_customer_id ON public.connections USING btree (customer_id); + + +-- +-- Name: idx_connection_requested_meters_rls_organization_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_connection_requested_meters_rls_organization_id ON public.connection_requested_meters USING btree (rls_organization_id); + + +-- +-- Name: idx_connections_rls_organization_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_connections_rls_organization_id ON public.connections USING btree (rls_organization_id); + + +-- +-- Name: idx_customer_grid_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_customer_grid_id ON public.customers USING btree (grid_id); + + +-- +-- Name: idx_customers_rls_organization_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_customers_rls_organization_id ON public.customers USING btree (rls_organization_id); + + +-- +-- Name: idx_dcus_rls_organization_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_dcus_rls_organization_id ON public.dcus USING btree (rls_organization_id); + + +-- +-- Name: idx_directive_batch_executions_rls_organization_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_directive_batch_executions_rls_organization_id ON public.directive_batch_executions USING btree (rls_organization_id); + + +-- +-- Name: idx_directive_batches_rls_organization_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_directive_batches_rls_organization_id ON public.directive_batches USING btree (rls_organization_id); + + +-- +-- Name: idx_directives_batch_status; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_directives_batch_status ON public.directives USING btree (directive_batch_execution_id, directive_status); + + +-- +-- Name: idx_directives_rls_organization_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_directives_rls_organization_id ON public.directives USING btree (rls_organization_id); + + +-- +-- Name: idx_energy_cabins_location_geom; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_energy_cabins_location_geom ON public.energy_cabins USING gist (location_geom); + + +-- +-- Name: idx_energy_cabins_rls_organization_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_energy_cabins_rls_organization_id ON public.energy_cabins USING btree (rls_organization_id); + + +-- +-- Name: idx_issues_open; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_issues_open ON public.issues USING btree (rls_organization_id, meter_id, created_at DESC) WHERE (issue_status = 'OPEN'::public.issue_status_enum); + + +-- +-- Name: idx_issues_rls_organization_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_issues_rls_organization_id ON public.issues USING btree (rls_organization_id); + + +-- +-- Name: idx_lorawan_batch_status; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_lorawan_batch_status ON public.lorawan_directives USING btree (directive_batch_execution_id, directive_status); + + +-- +-- Name: idx_lorawan_directives_rls_organization_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_lorawan_directives_rls_organization_id ON public.lorawan_directives USING btree (rls_organization_id); + + +-- +-- Name: idx_members_rls_organization_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_members_rls_organization_id ON public.members USING btree (rls_organization_id); + + +-- +-- Name: idx_meter_commissionings_rls_organization_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_meter_commissionings_rls_organization_id ON public.meter_commissionings USING btree (rls_organization_id); + + +-- +-- Name: idx_meter_connection_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_meter_connection_id ON public.meters USING btree (connection_id); + + +-- +-- Name: idx_meter_credit_transfers_rls_organization_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_meter_credit_transfers_rls_organization_id ON public.meter_credit_transfers USING btree (rls_organization_id); + + +-- +-- Name: idx_meter_dcu_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_meter_dcu_id ON public.meters USING btree (dcu_id); + + +-- +-- Name: idx_meter_interaction_batch_execution_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_meter_interaction_batch_execution_id ON public.meter_interactions USING btree (batch_execution_id, meter_interaction_status); + + +-- +-- Name: idx_meter_interaction_meter_commissioning_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_meter_interaction_meter_commissioning_id ON public.meter_interactions USING btree (meter_commissioning_id); + + +-- +-- Name: idx_meter_interaction_meter_id_extra; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_meter_interaction_meter_id_extra ON public.meter_interactions USING btree (meter_id, meter_interaction_type, created_at DESC); + + +-- +-- Name: idx_metering_hardware_install_sessions_rls_organization_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_metering_hardware_install_sessions_rls_organization_id ON public.metering_hardware_install_sessions USING btree (rls_organization_id); + + +-- +-- Name: idx_meters_rls_organization_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_meters_rls_organization_id ON public.meters USING btree (rls_organization_id); + + +-- +-- Name: idx_mppts_rls_organization_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_mppts_rls_organization_id ON public.mppts USING btree (rls_organization_id); + + +-- +-- Name: idx_notes_rls_organization_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_notes_rls_organization_id ON public.notes USING btree (rls_organization_id); + + +-- +-- Name: idx_orders_energy_topup_grid; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_orders_energy_topup_grid ON public.orders USING btree (historical_grid_id) WHERE (meta_order_type = 'ENERGY_TOPUP'::public.order_type_enum); + + +-- +-- Name: idx_orders_energy_topup_receiver; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_orders_energy_topup_receiver ON public.orders USING btree (meta_receiver_id) WHERE (meta_order_type = 'ENERGY_TOPUP'::public.order_type_enum); + + +-- +-- Name: idx_orders_historical_grid_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_orders_historical_grid_id ON public.orders USING btree (historical_grid_id); + + +-- +-- Name: idx_orders_meta_receiver_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_orders_meta_receiver_id ON public.orders USING btree (meta_receiver_id); + + +-- +-- Name: idx_orders_order_optimized; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_orders_order_optimized ON public.orders USING btree (updated_at DESC); + + +-- +-- Name: idx_orders_rls_organization_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_orders_rls_organization_id ON public.orders USING btree (rls_organization_id); + + +-- +-- Name: idx_orders_rls_sender_receiver; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_orders_rls_sender_receiver ON public.orders USING btree (meta_sender_type, meta_sender_id, meta_receiver_type, meta_receiver_id); + + +-- +-- Name: idx_pd_site_submissions_location_geom; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_pd_site_submissions_location_geom ON public.pd_site_submissions USING gist (location_geom); + + +-- +-- Name: idx_pd_site_submissions_outline_geom; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_pd_site_submissions_outline_geom ON public.pd_site_submissions USING gist (outline_geom); + + +-- +-- Name: idx_pd_sites_location_geom; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_pd_sites_location_geom ON public.pd_sites USING gist (location_geom); + + +-- +-- Name: idx_pd_sites_outline_geom; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_pd_sites_outline_geom ON public.pd_sites USING gist (outline_geom); + + +-- +-- Name: idx_poles_location_geom; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_poles_location_geom ON public.poles USING gist (location_geom); + + +-- +-- Name: idx_poles_rls_organization_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_poles_rls_organization_id ON public.poles USING btree (rls_organization_id); + + +-- +-- Name: idx_routers_rls_organization_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_routers_rls_organization_id ON public.routers USING btree (rls_organization_id); + + +-- +-- Name: idx_transactions_rls_organization_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_transactions_rls_organization_id ON public.transactions USING btree (rls_organization_id); + + +-- +-- Name: idx_wallets_rls_organization_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_wallets_rls_organization_id ON public.wallets USING btree (rls_organization_id); + + +-- +-- Name: device_logs append_rls_organization_id_by_device_id; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_by_device_id BEFORE INSERT ON public.device_logs FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_device_id(); + + +-- +-- Name: agents append_rls_organization_id_on_agent_insert; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_on_agent_insert BEFORE INSERT ON public.agents FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + + +-- +-- Name: connections append_rls_organization_id_on_connection_insert; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_on_connection_insert BEFORE INSERT ON public.connections FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_customer_id(); + + +-- +-- Name: customers append_rls_organization_id_on_customer_insert; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_on_customer_insert BEFORE INSERT ON public.customers FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + + +-- +-- Name: dcus append_rls_organization_id_on_dcus_insert; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_on_dcus_insert BEFORE INSERT ON public.dcus FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + + +-- +-- Name: devices append_rls_organization_id_on_devices_insert; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_on_devices_insert BEFORE INSERT ON public.devices FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + + +-- +-- Name: directive_batch_executions append_rls_organization_id_on_directive_batch_execution_insert; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_on_directive_batch_execution_insert BEFORE INSERT ON public.directive_batch_executions FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_directive_batch_id(); + + +-- +-- Name: directive_batches append_rls_organization_id_on_directive_batch_insert; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_on_directive_batch_insert BEFORE INSERT ON public.directive_batches FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + + +-- +-- Name: directives append_rls_organization_id_on_directive_insert; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_on_directive_insert BEFORE INSERT ON public.directives FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_meter_id(); + + +-- +-- Name: energy_cabins append_rls_organization_id_on_energy_cabin_insert; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_on_energy_cabin_insert BEFORE INSERT ON public.energy_cabins FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + + +-- +-- Name: issues append_rls_organization_id_on_issue_insert; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_on_issue_insert BEFORE INSERT ON public.issues FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_meter_id(); + + +-- +-- Name: lorawan_directives append_rls_organization_id_on_lorawan_directive_insert; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_on_lorawan_directive_insert BEFORE INSERT ON public.lorawan_directives FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_meter_id(); + + +-- +-- Name: members append_rls_organization_id_on_member_insert; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_on_member_insert BEFORE INSERT ON public.members FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_account_id(); + + +-- +-- Name: meter_commissionings append_rls_organization_id_on_meter_commissioning_insert; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_on_meter_commissioning_insert BEFORE INSERT ON public.meter_commissionings FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_metering_hardware_install_session(); + + +-- +-- Name: meter_credit_transfers append_rls_organization_id_on_meter_credit_transfer_insert; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_on_meter_credit_transfer_insert BEFORE INSERT ON public.meter_credit_transfers FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_receiver_meter_id(); + + +-- +-- Name: meters append_rls_organization_id_on_meter_insert; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_on_meter_insert BEFORE INSERT ON public.meters FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_connection_id(); + + +-- +-- Name: metering_hardware_install_sessions append_rls_organization_id_on_meter_install_session_insert; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_on_meter_install_session_insert BEFORE INSERT ON public.metering_hardware_install_sessions FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_dcu_id_or_meter_id(); + + +-- +-- Name: mppts append_rls_organization_id_on_mppt_insert; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_on_mppt_insert BEFORE INSERT ON public.mppts FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + + +-- +-- Name: notes append_rls_organization_id_on_note_insert; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_on_note_insert BEFORE INSERT ON public.notes FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_customer_id(); + + +-- +-- Name: poles append_rls_organization_id_on_pole_insert; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_on_pole_insert BEFORE INSERT ON public.poles FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + + +-- +-- Name: connection_requested_meters append_rls_organization_id_on_requested_connection_meters_inser; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_on_requested_connection_meters_inser BEFORE INSERT ON public.connection_requested_meters FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_connection_id(); + + +-- +-- Name: routers append_rls_organization_id_on_route_insert; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_on_route_insert BEFORE INSERT ON public.routers FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + + +-- +-- Name: transactions append_rls_organization_id_on_transaction_insert; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_on_transaction_insert BEFORE INSERT ON public.transactions FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_order_id(); + + +-- +-- Name: wallets append_rls_organization_id_on_wallet_insert; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER append_rls_organization_id_on_wallet_insert BEFORE INSERT ON public.wallets FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_customer_id_or_agent_id_or_connec(); + + +-- +-- Name: grids notify_make_about_is_fs_on_updated; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER notify_make_about_is_fs_on_updated AFTER UPDATE ON public.grids FOR EACH ROW EXECUTE FUNCTION public.notify_make_about_is_fs_on_updated(); + + +-- +-- Name: grids notify_make_about_is_hps_on_updated; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER notify_make_about_is_hps_on_updated AFTER UPDATE ON public.grids FOR EACH ROW EXECUTE FUNCTION public.notify_make_about_is_hps_on_updated(); + + +-- +-- Name: grids notify_make_about_kwh_tariff_essential_service_updated; Type: TRIGGER; Schema: public; Owner: postgres +-- + +CREATE TRIGGER notify_make_about_kwh_tariff_essential_service_updated AFTER UPDATE ON public.grids FOR EACH ROW EXECUTE FUNCTION public.notify_make_about_kwh_tariff_essential_service_updated(); + + +-- +-- Name: meters FK_04e1a755d5b760f681e5205557d; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "FK_04e1a755d5b760f681e5205557d" FOREIGN KEY (dcu_id) REFERENCES public.dcus(id); + + +-- +-- Name: orders FK_093ca3525311b8a12f6cf6b1c9b; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "FK_093ca3525311b8a12f6cf6b1c9b" FOREIGN KEY (directive_id) REFERENCES public.directives(id); + + +-- +-- Name: mppts FK_0a681c1d6d0a67331bbc6956427; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.mppts + ADD CONSTRAINT "FK_0a681c1d6d0a67331bbc6956427" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + + +-- +-- Name: orders FK_0b03d1bc1cff784570333129e63; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "FK_0b03d1bc1cff784570333129e63" FOREIGN KEY (historical_grid_id) REFERENCES public.grids(id); + + +-- +-- Name: transactions FK_0b171330be0cb621f8d73b87a9e; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.transactions + ADD CONSTRAINT "FK_0b171330be0cb621f8d73b87a9e" FOREIGN KEY (wallet_id) REFERENCES public.wallets(id); + + +-- +-- Name: accounts FK_13bf998d703e7d4c7b9a90f4f76; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.accounts + ADD CONSTRAINT "FK_13bf998d703e7d4c7b9a90f4f76" FOREIGN KEY (supabase_id) REFERENCES auth.users(id); + + +-- +-- Name: audits FK_18c521072a6ca9d57365ffb24fe; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_18c521072a6ca9d57365ffb24fe" FOREIGN KEY (agent_id) REFERENCES public.agents(id); + + +-- +-- Name: audits FK_236a383b1007cf64f0c8c7f6534; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_236a383b1007cf64f0c8c7f6534" FOREIGN KEY (customer_id) REFERENCES public.customers(id); + + +-- +-- Name: accounts FK_238d61e0f8ac37278f726efac20; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.accounts + ADD CONSTRAINT "FK_238d61e0f8ac37278f726efac20" FOREIGN KEY (organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: dcus FK_254c3db39098da62eb3f96f0006; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.dcus + ADD CONSTRAINT "FK_254c3db39098da62eb3f96f0006" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + + +-- +-- Name: directives FK_259bc4fc1c63035217ee41a719d; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.directives + ADD CONSTRAINT "FK_259bc4fc1c63035217ee41a719d" FOREIGN KEY (meter_commissioning_id) REFERENCES public.meter_commissionings(id); + + +-- +-- Name: directives FK_276c89b475e9805d3322a36657a; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.directives + ADD CONSTRAINT "FK_276c89b475e9805d3322a36657a" FOREIGN KEY (order_id) REFERENCES public.orders(id); + + +-- +-- Name: poles FK_2c21d74185234b0dd39588a4d36; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.poles + ADD CONSTRAINT "FK_2c21d74185234b0dd39588a4d36" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + + +-- +-- Name: audits FK_2d52a7fc85cfbb0421af977766e; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_2d52a7fc85cfbb0421af977766e" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + + +-- +-- Name: audits FK_2fc5f64b328675b5203d54c7929; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_2fc5f64b328675b5203d54c7929" FOREIGN KEY (connection_id) REFERENCES public.connections(id); + + +-- +-- Name: audits FK_336b627b254b70b0702436e6aff; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_336b627b254b70b0702436e6aff" FOREIGN KEY (author_id) REFERENCES public.accounts(id); + + +-- +-- Name: metering_hardware_install_sessions FK_35239534a89f0d6dab2612645eb; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "FK_35239534a89f0d6dab2612645eb" FOREIGN KEY (author_id) REFERENCES public.accounts(id); + + +-- +-- Name: notes FK_35b89a50cb9203dccff44136519; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.notes + ADD CONSTRAINT "FK_35b89a50cb9203dccff44136519" FOREIGN KEY (author_id) REFERENCES public.accounts(id); + + +-- +-- Name: connections FK_37f79eb1e29a53cc582fbb805e0; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.connections + ADD CONSTRAINT "FK_37f79eb1e29a53cc582fbb805e0" FOREIGN KEY (customer_id) REFERENCES public.customers(id); + + +-- +-- Name: ussd_sessions FK_3a2606011b8d9f1de07de2bf0a8; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.ussd_sessions + ADD CONSTRAINT "FK_3a2606011b8d9f1de07de2bf0a8" FOREIGN KEY (meter_id) REFERENCES public.meters(id); + + +-- +-- Name: meter_credit_transfers FK_3b4ebdcd826c46e4b5c165a0840; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meter_credit_transfers + ADD CONSTRAINT "FK_3b4ebdcd826c46e4b5c165a0840" FOREIGN KEY (sender_meter_id) REFERENCES public.meters(id); + + +-- +-- Name: routers FK_3c3ddc58369a9a8147776e39f40; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.routers + ADD CONSTRAINT "FK_3c3ddc58369a9a8147776e39f40" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + + +-- +-- Name: transactions FK_3cb0558ed36997f1d9ecc1118e7; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.transactions + ADD CONSTRAINT "FK_3cb0558ed36997f1d9ecc1118e7" FOREIGN KEY (order_id) REFERENCES public.orders(id); + + +-- +-- Name: directives FK_3f3cc652d521872dbfe604b05b6; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.directives + ADD CONSTRAINT "FK_3f3cc652d521872dbfe604b05b6" FOREIGN KEY (meter_credit_transfer_id) REFERENCES public.meter_credit_transfers(id); + + +-- +-- Name: metering_hardware_install_sessions FK_3f770c153b3333ad1d29cfbf784; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "FK_3f770c153b3333ad1d29cfbf784" FOREIGN KEY (meter_id) REFERENCES public.meters(id); + + +-- +-- Name: grids FK_433ef6a589cd535dff42b34612e; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.grids + ADD CONSTRAINT "FK_433ef6a589cd535dff42b34612e" FOREIGN KEY (organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: bank_accounts FK_482543ba26483726aaa00d39174; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.bank_accounts + ADD CONSTRAINT "FK_482543ba26483726aaa00d39174" FOREIGN KEY (bank_id) REFERENCES public.banks(id); + + +-- +-- Name: audits FK_4a300197465db92edfc5563d20b; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_4a300197465db92edfc5563d20b" FOREIGN KEY (meter_id) REFERENCES public.meters(id); + + +-- +-- Name: ussd_sessions FK_4cad92626a62976ac0b49244d2e; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.ussd_sessions + ADD CONSTRAINT "FK_4cad92626a62976ac0b49244d2e" FOREIGN KEY (bank_id) REFERENCES public.banks(id); + + +-- +-- Name: member_feature FK_4da956d70d1de38087b251b9978; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.member_feature + ADD CONSTRAINT "FK_4da956d70d1de38087b251b9978" FOREIGN KEY (feature_id) REFERENCES public.features(id) ON UPDATE CASCADE ON DELETE CASCADE; + + +-- +-- Name: metering_hardware_install_sessions FK_4eb64f5decb7250cfa993410c1e; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "FK_4eb64f5decb7250cfa993410c1e" FOREIGN KEY (last_meter_commissioning_id) REFERENCES public.meter_commissionings(id); + + +-- +-- Name: customers FK_51a88ee1d4fceb047e7cfda3baa; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.customers + ADD CONSTRAINT "FK_51a88ee1d4fceb047e7cfda3baa" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + + +-- +-- Name: meters FK_5606d6ec5ab568377509edc5267; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "FK_5606d6ec5ab568377509edc5267" FOREIGN KEY (last_encountered_issue_id) REFERENCES public.issues(id); + + +-- +-- Name: orders FK_576b0e9af0bec469bff33b965aa; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "FK_576b0e9af0bec469bff33b965aa" FOREIGN KEY (receiver_wallet_id) REFERENCES public.wallets(id); + + +-- +-- Name: metering_hardware_imports FK_5c2bb31b2e45c3f1da0b72071a2; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.metering_hardware_imports + ADD CONSTRAINT "FK_5c2bb31b2e45c3f1da0b72071a2" FOREIGN KEY (metering_hardware_install_session_id) REFERENCES public.metering_hardware_install_sessions(id); + + +-- +-- Name: directive_batches FK_5c44fae05dc443720f62664e563; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.directive_batches + ADD CONSTRAINT "FK_5c44fae05dc443720f62664e563" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + + +-- +-- Name: notes FK_5ec9536f852f1923097ba1ecdac; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.notes + ADD CONSTRAINT "FK_5ec9536f852f1923097ba1ecdac" FOREIGN KEY (meter_id) REFERENCES public.meters(id); + + +-- +-- Name: orders FK_60816230893327daacc86ab41c8; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "FK_60816230893327daacc86ab41c8" FOREIGN KEY (author_id) REFERENCES public.accounts(id); + + +-- +-- Name: directives FK_637c697dc2877f08538b2fa02f5; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.directives + ADD CONSTRAINT "FK_637c697dc2877f08538b2fa02f5" FOREIGN KEY (directive_watchdog_session_id) REFERENCES public.directive_watchdog_sessions(id); + + +-- +-- Name: wallets FK_6580899a2293de27787376887fa; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "FK_6580899a2293de27787376887fa" FOREIGN KEY (customer_id) REFERENCES public.customers(id); + + +-- +-- Name: meter_commissionings FK_6684f0790c54ed3d441f78df91a; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meter_commissionings + ADD CONSTRAINT "FK_6684f0790c54ed3d441f78df91a" FOREIGN KEY (metering_hardware_install_session_id) REFERENCES public.metering_hardware_install_sessions(id); + + +-- +-- Name: directives FK_6c4e7feadae61cb27761734b11d; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.directives + ADD CONSTRAINT "FK_6c4e7feadae61cb27761734b11d" FOREIGN KEY (meter_id) REFERENCES public.meters(id); + + +-- +-- Name: directive_batches FK_6e26cb6e966f484f0f897127c84; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.directive_batches + ADD CONSTRAINT "FK_6e26cb6e966f484f0f897127c84" FOREIGN KEY (author_id) REFERENCES public.accounts(id); + + +-- +-- Name: payouts FK_7796c1df57eeefa9173325627c4; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.payouts + ADD CONSTRAINT "FK_7796c1df57eeefa9173325627c4" FOREIGN KEY (bank_account_id) REFERENCES public.bank_accounts(id); + + +-- +-- Name: directive_batch_executions FK_7b0737005e7c358392d87ebb329; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.directive_batch_executions + ADD CONSTRAINT "FK_7b0737005e7c358392d87ebb329" FOREIGN KEY (directive_batch_id) REFERENCES public.directive_batches(id); + + +-- +-- Name: issues FK_7b15f3fbbedf51a1a3d21583b7e; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.issues + ADD CONSTRAINT "FK_7b15f3fbbedf51a1a3d21583b7e" FOREIGN KEY (meter_id) REFERENCES public.meters(id); + + +-- +-- Name: meters FK_86d4557a79e374b5c55cb3b66d8; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "FK_86d4557a79e374b5c55cb3b66d8" FOREIGN KEY (last_metering_hardware_install_session_id) REFERENCES public.metering_hardware_install_sessions(id); + + +-- +-- Name: wallets FK_8db1f4e4f8122bd25d50ad96b26; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "FK_8db1f4e4f8122bd25d50ad96b26" FOREIGN KEY (meter_id) REFERENCES public.meters(id); + + +-- +-- Name: orders FK_8f12fe9c7b078122adcae80375d; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "FK_8f12fe9c7b078122adcae80375d" FOREIGN KEY (sender_wallet_id) REFERENCES public.wallets(id); + + +-- +-- Name: directives FK_9064328678b805e99750773387d; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.directives + ADD CONSTRAINT "FK_9064328678b805e99750773387d" FOREIGN KEY (retry_of_directive_id) REFERENCES public.directives(id); + + +-- +-- Name: orders FK_92081072063eaf51300ab6c267d; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "FK_92081072063eaf51300ab6c267d" FOREIGN KEY (ussd_session_id) REFERENCES public.ussd_sessions(id); + + +-- +-- Name: dcus FK_9334b587e6e5291f8ccd1b11c44; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.dcus + ADD CONSTRAINT "FK_9334b587e6e5291f8ccd1b11c44" FOREIGN KEY (last_metering_hardware_install_session_id) REFERENCES public.metering_hardware_install_sessions(id); + + +-- +-- Name: notifications FK_982f77636cb1da8e76745949530; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.notifications + ADD CONSTRAINT "FK_982f77636cb1da8e76745949530" FOREIGN KEY (notification_parameter_id) REFERENCES public.notification_parameters(id); + + +-- +-- Name: agents FK_9bb7cb7efc780ec4d3dec34354f; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.agents + ADD CONSTRAINT "FK_9bb7cb7efc780ec4d3dec34354f" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + + +-- +-- Name: meters FK_a11871575f7ddb95e567d842bc6; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "FK_a11871575f7ddb95e567d842bc6" FOREIGN KEY (pole_id) REFERENCES public.poles(id); + + +-- +-- Name: notifications FK_a1ec3b4b4f2017665b534e60256; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.notifications + ADD CONSTRAINT "FK_a1ec3b4b4f2017665b534e60256" FOREIGN KEY (account_id) REFERENCES public.accounts(id); + + +-- +-- Name: orders FK_a53c58bdb5ae0193f17497c81b4; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "FK_a53c58bdb5ae0193f17497c81b4" FOREIGN KEY (meter_credit_transfer_id) REFERENCES public.meter_credit_transfers(id); + + +-- +-- Name: members FK_a9da2a35fdd2cd46a7bfc57fd73; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.members + ADD CONSTRAINT "FK_a9da2a35fdd2cd46a7bfc57fd73" FOREIGN KEY (busy_commissioning_id) REFERENCES public.grids(id); + + +-- +-- Name: audits FK_abde505f056e2f46c5df2c12491; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_abde505f056e2f46c5df2c12491" FOREIGN KEY (organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: meter_credit_transfers FK_b196e2b2b291716e18a11611350; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meter_credit_transfers + ADD CONSTRAINT "FK_b196e2b2b291716e18a11611350" FOREIGN KEY (author_id) REFERENCES public.accounts(id); + + +-- +-- Name: energy_cabins FK_ba81ffc1506025a549849f06f36; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.energy_cabins + ADD CONSTRAINT "FK_ba81ffc1506025a549849f06f36" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + + +-- +-- Name: metering_hardware_install_sessions FK_be64a09dc738420a409cb960264; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "FK_be64a09dc738420a409cb960264" FOREIGN KEY (last_metering_hardware_import_id) REFERENCES public.metering_hardware_imports(id); + + +-- +-- Name: issues FK_be8d64bd0c97a739ac323dde9dd; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.issues + ADD CONSTRAINT "FK_be8d64bd0c97a739ac323dde9dd" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + + +-- +-- Name: issues FK_bf46db804a794d1d09eb050ac77; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.issues + ADD CONSTRAINT "FK_bf46db804a794d1d09eb050ac77" FOREIGN KEY (mppt_id) REFERENCES public.mppts(id); + + +-- +-- Name: ussd_session_hops FK_bf89299bd6534528adad645b7a5; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.ussd_session_hops + ADD CONSTRAINT "FK_bf89299bd6534528adad645b7a5" FOREIGN KEY (ussd_session_id) REFERENCES public.ussd_sessions(id); + + +-- +-- Name: notifications FK_c1053eec8005016c7d9febdc484; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.notifications + ADD CONSTRAINT "FK_c1053eec8005016c7d9febdc484" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + + +-- +-- Name: member_feature FK_c312f698867204bf4f1fd149c91; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.member_feature + ADD CONSTRAINT "FK_c312f698867204bf4f1fd149c91" FOREIGN KEY (member_id) REFERENCES public.members(id) ON UPDATE CASCADE ON DELETE CASCADE; + + +-- +-- Name: notifications FK_cb7b1fb018b296f2107e998b2ff; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.notifications + ADD CONSTRAINT "FK_cb7b1fb018b296f2107e998b2ff" FOREIGN KEY (organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: bank_accounts FK_cc20105b139589c697648c925c3; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.bank_accounts + ADD CONSTRAINT "FK_cc20105b139589c697648c925c3" FOREIGN KEY (organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: meters FK_d281c6e5f391b8de5b221032da6; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "FK_d281c6e5f391b8de5b221032da6" FOREIGN KEY (connection_id) REFERENCES public.connections(id); + + +-- +-- Name: connection_requested_meters FK_d390197863e9a88be5e122d9695; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.connection_requested_meters + ADD CONSTRAINT "FK_d390197863e9a88be5e122d9695" FOREIGN KEY (connection_id) REFERENCES public.connections(id); + + +-- +-- Name: api_keys FK_decaf331589778e33441b2a8d9e; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.api_keys + ADD CONSTRAINT "FK_decaf331589778e33441b2a8d9e" FOREIGN KEY (account_id) REFERENCES public.accounts(id); + + +-- +-- Name: agents FK_df520decc9e003a843d8edd9867; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.agents + ADD CONSTRAINT "FK_df520decc9e003a843d8edd9867" FOREIGN KEY (account_id) REFERENCES public.accounts(id); + + +-- +-- Name: notes FK_e04c36c14bc9f01f84cd7655b68; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.notes + ADD CONSTRAINT "FK_e04c36c14bc9f01f84cd7655b68" FOREIGN KEY (customer_id) REFERENCES public.customers(id); + + +-- +-- Name: payouts FK_e2e6de2a3c003b36f840829079a; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.payouts + ADD CONSTRAINT "FK_e2e6de2a3c003b36f840829079a" FOREIGN KEY (approved_by_account_id) REFERENCES public.accounts(id); + + +-- +-- Name: audits FK_e3f3409859562fbe10b78d1399e; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_e3f3409859562fbe10b78d1399e" FOREIGN KEY (member_id) REFERENCES public.members(id); + + +-- +-- Name: wallets FK_e63e504d8e35ef37a2c56b75eb9; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "FK_e63e504d8e35ef37a2c56b75eb9" FOREIGN KEY (connection_id) REFERENCES public.connections(id); + + +-- +-- Name: meter_credit_transfers FK_e83e8905a777a669de1c2391b77; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meter_credit_transfers + ADD CONSTRAINT "FK_e83e8905a777a669de1c2391b77" FOREIGN KEY (receiver_meter_id) REFERENCES public.meters(id); + + +-- +-- Name: payouts FK_e8bc1a446b4df957b8d37af1cd0; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.payouts + ADD CONSTRAINT "FK_e8bc1a446b4df957b8d37af1cd0" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + + +-- +-- Name: audits FK_eb582acfd9d83af268d328e0b79; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_eb582acfd9d83af268d328e0b79" FOREIGN KEY (dcu_id) REFERENCES public.dcus(id); + + +-- +-- Name: customers FK_ebcc29963874e55053e8ee80be5; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.customers + ADD CONSTRAINT "FK_ebcc29963874e55053e8ee80be5" FOREIGN KEY (account_id) REFERENCES public.accounts(id); + + +-- +-- Name: ussd_sessions FK_ede50d58fa445bfa012135249fd; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.ussd_sessions + ADD CONSTRAINT "FK_ede50d58fa445bfa012135249fd" FOREIGN KEY (account_id) REFERENCES public.accounts(id); + + +-- +-- Name: notes FK_f1a5ea7b77453030e2a1df27479; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.notes + ADD CONSTRAINT "FK_f1a5ea7b77453030e2a1df27479" FOREIGN KEY (connection_id) REFERENCES public.connections(id); + + +-- +-- Name: directives FK_f3a84a3f4b619b1ca8c179fff6c; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.directives + ADD CONSTRAINT "FK_f3a84a3f4b619b1ca8c179fff6c" FOREIGN KEY (author_id) REFERENCES public.accounts(id); + + +-- +-- Name: metering_hardware_install_sessions FK_f3c53f4b922a011aa53d9e0b1ad; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "FK_f3c53f4b922a011aa53d9e0b1ad" FOREIGN KEY (dcu_id) REFERENCES public.dcus(id); + + +-- +-- Name: wallets FK_f499c61c6d6a0ac3f794d966edc; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "FK_f499c61c6d6a0ac3f794d966edc" FOREIGN KEY (organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: wallets FK_f5782e05e8688f0cfbb5c4a52ce; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "FK_f5782e05e8688f0cfbb5c4a52ce" FOREIGN KEY (agent_id) REFERENCES public.agents(id); + + +-- +-- Name: directives FK_f9e3439b6ea7096d6a9d85c4503; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.directives + ADD CONSTRAINT "FK_f9e3439b6ea7096d6a9d85c4503" FOREIGN KEY (directive_batch_execution_id) REFERENCES public.directive_batch_executions(id); + + +-- +-- Name: members FK_fd9dfb97e21b75fc45d42aa614a; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.members + ADD CONSTRAINT "FK_fd9dfb97e21b75fc45d42aa614a" FOREIGN KEY (account_id) REFERENCES public.accounts(id); + + +-- +-- Name: agents agents_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.agents + ADD CONSTRAINT agents_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: connection_requested_meters connection_requested_meters_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.connection_requested_meters + ADD CONSTRAINT connection_requested_meters_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: connections connections_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.connections + ADD CONSTRAINT connections_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: customers customers_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.customers + ADD CONSTRAINT customers_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: dcus dcus_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.dcus + ADD CONSTRAINT dcus_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: device_logs device_logs_device_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.device_logs + ADD CONSTRAINT device_logs_device_id_fkey FOREIGN KEY (device_id) REFERENCES public.devices(id); + + +-- +-- Name: device_logs device_logs_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.device_logs + ADD CONSTRAINT device_logs_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: devices devices_device_type_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.devices + ADD CONSTRAINT devices_device_type_id_fkey FOREIGN KEY (device_type_id) REFERENCES public.device_types(id); + + +-- +-- Name: devices devices_grid_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.devices + ADD CONSTRAINT devices_grid_id_fkey FOREIGN KEY (grid_id) REFERENCES public.grids(id); + + +-- +-- Name: devices devices_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.devices + ADD CONSTRAINT devices_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: directive_batch_executions directive_batch_executions_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.directive_batch_executions + ADD CONSTRAINT directive_batch_executions_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: directive_batches directive_batches_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.directive_batches + ADD CONSTRAINT directive_batches_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: directives directives_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.directives + ADD CONSTRAINT directives_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: energy_cabins energy_cabins_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.energy_cabins + ADD CONSTRAINT energy_cabins_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: issues issues_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.issues + ADD CONSTRAINT issues_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: lorawan_directives lorawan_directives_author_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.lorawan_directives + ADD CONSTRAINT lorawan_directives_author_id_fkey FOREIGN KEY (author_id) REFERENCES public.accounts(id); + + +-- +-- Name: lorawan_directives lorawan_directives_directive_batch_execution_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.lorawan_directives + ADD CONSTRAINT lorawan_directives_directive_batch_execution_id_fkey FOREIGN KEY (directive_batch_execution_id) REFERENCES public.directive_batch_executions(id); + + +-- +-- Name: lorawan_directives lorawan_directives_directive_watchdog_session_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.lorawan_directives + ADD CONSTRAINT lorawan_directives_directive_watchdog_session_id_fkey FOREIGN KEY (directive_watchdog_session_id) REFERENCES public.directive_watchdog_sessions(id); + + +-- +-- Name: lorawan_directives lorawan_directives_meter_commissioning_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.lorawan_directives + ADD CONSTRAINT lorawan_directives_meter_commissioning_id_fkey FOREIGN KEY (meter_commissioning_id) REFERENCES public.meter_commissionings(id); + + +-- +-- Name: lorawan_directives lorawan_directives_meter_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.lorawan_directives + ADD CONSTRAINT lorawan_directives_meter_id_fkey FOREIGN KEY (meter_id) REFERENCES public.meters(id); + + +-- +-- Name: lorawan_directives lorawan_directives_order_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.lorawan_directives + ADD CONSTRAINT lorawan_directives_order_id_fkey FOREIGN KEY (order_id) REFERENCES public.orders(id); + + +-- +-- Name: lorawan_directives lorawan_directives_retry_of_directive_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.lorawan_directives + ADD CONSTRAINT lorawan_directives_retry_of_directive_id_fkey FOREIGN KEY (retry_of_directive_id) REFERENCES public.lorawan_directives(id); + + +-- +-- Name: lorawan_directives lorawan_directives_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.lorawan_directives + ADD CONSTRAINT lorawan_directives_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: members members_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.members + ADD CONSTRAINT members_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: meter_commissionings meter_commissionings_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meter_commissionings + ADD CONSTRAINT meter_commissionings_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: meter_credit_transfers meter_credit_transfers_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meter_credit_transfers + ADD CONSTRAINT meter_credit_transfers_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: meter_interactions meter_interactions_batch_execution_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meter_interactions + ADD CONSTRAINT meter_interactions_batch_execution_id_fkey FOREIGN KEY (batch_execution_id) REFERENCES public.directive_batch_executions(id); + + +-- +-- Name: meter_interactions meter_interactions_meter_commissioning_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meter_interactions + ADD CONSTRAINT meter_interactions_meter_commissioning_id_fkey FOREIGN KEY (meter_commissioning_id) REFERENCES public.meter_commissionings(id); + + +-- +-- Name: meter_interactions meter_interactions_meter_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meter_interactions + ADD CONSTRAINT meter_interactions_meter_id_fkey FOREIGN KEY (meter_id) REFERENCES public.meters(id); + + +-- +-- Name: meter_interactions meter_interactions_order_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meter_interactions + ADD CONSTRAINT meter_interactions_order_id_fkey FOREIGN KEY (order_id) REFERENCES public.orders(id); + + +-- +-- Name: metering_hardware_imports metering_hardware_imports_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.metering_hardware_imports + ADD CONSTRAINT metering_hardware_imports_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: metering_hardware_install_sessions metering_hardware_install_sessions_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT metering_hardware_install_sessions_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: meters meters_device_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT meters_device_id_fkey FOREIGN KEY (device_id) REFERENCES public.devices(id); + + +-- +-- Name: meters meters_rls_grid_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT meters_rls_grid_id_fkey FOREIGN KEY (rls_grid_id) REFERENCES public.grids(id); + + +-- +-- Name: meters meters_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT meters_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: mppts mppts_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.mppts + ADD CONSTRAINT mppts_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: notes notes_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.notes + ADD CONSTRAINT notes_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: orders orders_lorawan_directive_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT orders_lorawan_directive_id_fkey FOREIGN KEY (lorawan_directive_id) REFERENCES public.lorawan_directives(id); + + +-- +-- Name: orders orders_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT orders_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: pd_action_templates pd_action_templates_pd_document_template_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_action_templates + ADD CONSTRAINT pd_action_templates_pd_document_template_id_fkey FOREIGN KEY (pd_document_template_id) REFERENCES public.pd_document_templates(id); + + +-- +-- Name: pd_action_templates pd_action_templates_pd_flow_template_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_action_templates + ADD CONSTRAINT pd_action_templates_pd_flow_template_id_fkey FOREIGN KEY (pd_flow_template_id) REFERENCES public.pd_flow_templates(id); + + +-- +-- Name: pd_actions pd_actions_latest_pd_document_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_actions + ADD CONSTRAINT pd_actions_latest_pd_document_id_fkey FOREIGN KEY (latest_pd_document_id) REFERENCES public.pd_documents(id); + + +-- +-- Name: pd_actions pd_actions_pd_action_template_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_actions + ADD CONSTRAINT pd_actions_pd_action_template_id_fkey FOREIGN KEY (pd_action_template_id) REFERENCES public.pd_action_templates(id); + + +-- +-- Name: pd_actions pd_actions_pd_flow_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_actions + ADD CONSTRAINT pd_actions_pd_flow_id_fkey FOREIGN KEY (pd_flow_id) REFERENCES public.pd_flows(id); + + +-- +-- Name: pd_actions pd_actions_pd_section_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_actions + ADD CONSTRAINT pd_actions_pd_section_id_fkey FOREIGN KEY (pd_section_id) REFERENCES public.pd_actions(id); + + +-- +-- Name: pd_audits pd_audits_author_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_audits + ADD CONSTRAINT pd_audits_author_id_fkey FOREIGN KEY (author_id) REFERENCES public.accounts(id); + + +-- +-- Name: pd_audits pd_audits_pd_action_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_audits + ADD CONSTRAINT pd_audits_pd_action_id_fkey FOREIGN KEY (pd_action_id) REFERENCES public.pd_actions(id); + + +-- +-- Name: pd_documents pd_documents_pd_action_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_documents + ADD CONSTRAINT pd_documents_pd_action_id_fkey FOREIGN KEY (pd_action_id) REFERENCES public.pd_actions(id); + + +-- +-- Name: pd_flows pd_flows_pd_flow_template_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_flows + ADD CONSTRAINT pd_flows_pd_flow_template_id_fkey FOREIGN KEY (pd_flow_template_id) REFERENCES public.pd_flow_templates(id); + + +-- +-- Name: pd_site_submissions pd_site_submissions_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_site_submissions + ADD CONSTRAINT pd_site_submissions_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: pd_sites pd_sites_operations_grid_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_sites + ADD CONSTRAINT pd_sites_operations_grid_id_fkey FOREIGN KEY (operations_grid_id) REFERENCES public.grids(id); + + +-- +-- Name: pd_sites pd_sites_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_sites + ADD CONSTRAINT pd_sites_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: pd_sites pd_sites_pd_flow_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.pd_sites + ADD CONSTRAINT pd_sites_pd_flow_id_fkey FOREIGN KEY (pd_flow_id) REFERENCES public.pd_flows(id); + + +-- +-- Name: poles poles_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.poles + ADD CONSTRAINT poles_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: routers routers_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.routers + ADD CONSTRAINT routers_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: transactions transactions_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.transactions + ADD CONSTRAINT transactions_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: wallets wallets_rls_organization_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT wallets_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + + +-- +-- Name: lorawan_directives Allow Grafana to Select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow Grafana to Select" ON public.lorawan_directives FOR SELECT TO grafana_readonly USING (true); + + +-- +-- Name: connections Allow Grafana to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow Grafana to select" ON public.connections FOR SELECT TO grafana_readonly USING (true); + + +-- +-- Name: customers Allow Grafana to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow Grafana to select" ON public.customers FOR SELECT TO grafana_readonly USING (true); + + +-- +-- Name: dcus Allow Grafana to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow Grafana to select" ON public.dcus FOR SELECT TO grafana_readonly USING (true); + + +-- +-- Name: directive_batch_executions Allow Grafana to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow Grafana to select" ON public.directive_batch_executions FOR SELECT TO grafana_readonly USING (true); + + +-- +-- Name: directive_batches Allow Grafana to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow Grafana to select" ON public.directive_batches FOR SELECT TO grafana_readonly USING (true); + + +-- +-- Name: directives Allow Grafana to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow Grafana to select" ON public.directives FOR SELECT TO grafana_readonly USING (true); + + +-- +-- Name: meters Allow Grafana to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow Grafana to select" ON public.meters FOR SELECT TO grafana_readonly USING (true); + + +-- +-- Name: orders Allow Grafana to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow Grafana to select" ON public.orders FOR SELECT TO grafana_readonly USING (true); + + +-- +-- Name: organizations Allow Grafana to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow Grafana to select" ON public.organizations FOR SELECT TO grafana_readonly USING (true); + + +-- +-- Name: transactions Allow Grafana to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow Grafana to select" ON public.transactions FOR SELECT TO grafana_readonly USING (true); + + +-- +-- Name: wallets Allow Grafana to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow Grafana to select" ON public.wallets FOR SELECT TO grafana_readonly USING (true); + + +-- +-- Name: grids Allow Grafana to select all; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow Grafana to select all" ON public.grids FOR SELECT TO grafana_readonly USING (true); + + +-- +-- Name: accounts Allow Make to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow Make to select" ON public.accounts FOR SELECT TO make_readonly USING (true); + + +-- +-- Name: dcus Allow Make to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow Make to select" ON public.dcus FOR SELECT TO make_readonly USING (true); + + +-- +-- Name: grids Allow Make to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow Make to select" ON public.grids FOR SELECT TO make_readonly USING (true); + + +-- +-- Name: organizations Allow Make to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow Make to select" ON public.organizations FOR SELECT TO make_readonly USING (true); + + +-- +-- Name: connections Allow NXT Grid to CRUD; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to CRUD" ON public.connections TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: grids Allow NXT Grid to insert; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to insert" ON public.grids FOR INSERT TO authenticated WITH CHECK (public.rls_check_if_nxt_member()); + + +-- +-- Name: meters Allow NXT Grid to insert; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to insert" ON public.meters FOR INSERT TO authenticated WITH CHECK (public.rls_check_if_nxt_member()); + + +-- +-- Name: notes Allow NXT Grid to insert; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to insert" ON public.notes FOR INSERT TO authenticated WITH CHECK (public.rls_check_if_nxt_member()); + + +-- +-- Name: organizations Allow NXT Grid to insert; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to insert" ON public.organizations FOR INSERT TO authenticated WITH CHECK (public.rls_check_if_nxt_member()); + + +-- +-- Name: pd_actions Allow NXT Grid to insert; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to insert" ON public.pd_actions FOR INSERT TO authenticated WITH CHECK (public.rls_check_if_nxt_member()); + + +-- +-- Name: pd_documents Allow NXT Grid to insert; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to insert" ON public.pd_documents FOR INSERT TO authenticated WITH CHECK (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: pd_flows Allow NXT Grid to insert; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to insert" ON public.pd_flows FOR INSERT TO authenticated WITH CHECK (public.rls_check_if_nxt_member()); + + +-- +-- Name: pd_sites Allow NXT Grid to insert; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to insert" ON public.pd_sites FOR INSERT TO authenticated WITH CHECK (public.rls_check_if_nxt_member()); + + +-- +-- Name: poles Allow NXT Grid to insert; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to insert" ON public.poles FOR INSERT TO authenticated WITH CHECK (public.rls_check_if_nxt_member()); + + +-- +-- Name: wallets Allow NXT Grid to insert; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to insert" ON public.wallets FOR INSERT WITH CHECK (public.rls_check_if_nxt_member()); + + +-- +-- Name: accounts Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.accounts FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: agents Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.agents FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: connection_requested_meters Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.connection_requested_meters FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: connections Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.connections FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: customers Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.customers FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: dcus Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.dcus FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: device_logs Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.device_logs FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: directive_batch_executions Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.directive_batch_executions FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: directive_batches Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.directive_batches FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: directives Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.directives FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: energy_cabins Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.energy_cabins FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: features Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.features FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: grids Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.grids FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: issues Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.issues FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: lorawan_directives Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.lorawan_directives FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: members Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.members FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: meter_commissionings Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.meter_commissionings FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: meter_interactions Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.meter_interactions FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: metering_hardware_install_sessions Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.metering_hardware_install_sessions FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: meters Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.meters FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: mppts Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.mppts FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: notes Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.notes FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: orders Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.orders FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: organizations Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.organizations FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: payouts Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.payouts FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: pd_actions Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.pd_actions FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: pd_flows Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.pd_flows FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: pd_sites Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.pd_sites FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: poles Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.poles FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: routers Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.routers FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: transactions Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.transactions FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: wallets Allow NXT Grid to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to select" ON public.wallets FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: accounts Allow NXT Grid to update; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to update" ON public.accounts FOR UPDATE TO authenticated USING (public.rls_check_if_nxt_member()); + + +-- +-- Name: connections Allow NXT Grid to update; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to update" ON public.connections FOR UPDATE TO authenticated USING (public.rls_check_if_nxt_member()); + + +-- +-- Name: features Allow NXT Grid to update; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to update" ON public.features FOR UPDATE TO authenticated USING (public.rls_check_if_nxt_member()); + + +-- +-- Name: grids Allow NXT Grid to update; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to update" ON public.grids FOR UPDATE TO authenticated USING (public.rls_check_if_nxt_member()); + + +-- +-- Name: meters Allow NXT Grid to update; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to update" ON public.meters FOR UPDATE TO authenticated USING (public.rls_check_if_nxt_member()); + + +-- +-- Name: organizations Allow NXT Grid to update; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to update" ON public.organizations FOR UPDATE TO authenticated USING (public.rls_check_if_nxt_member()); + + +-- +-- Name: pd_actions Allow NXT Grid to update; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to update" ON public.pd_actions FOR UPDATE TO authenticated USING (public.rls_check_if_nxt_member()); + + +-- +-- Name: pd_flows Allow NXT Grid to update; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to update" ON public.pd_flows FOR UPDATE TO authenticated USING (public.rls_check_if_nxt_member()); + + +-- +-- Name: pd_site_submissions Allow NXT Grid to update; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to update" ON public.pd_site_submissions FOR UPDATE TO authenticated USING (public.rls_check_if_nxt_member()); + + +-- +-- Name: pd_sites Allow NXT Grid to update; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT Grid to update" ON public.pd_sites FOR UPDATE TO authenticated USING (public.rls_check_if_nxt_member()); + + +-- +-- Name: devices Allow NXT to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow NXT to select" ON public.devices FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_nxt_member() AS rls_check_if_nxt_member)); + + +-- +-- Name: audits Allow authenticated to do everything; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow authenticated to do everything" ON public.audits USING (true); + + +-- +-- Name: device_types Allow authenticated to insert; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow authenticated to insert" ON public.device_types FOR INSERT TO authenticated WITH CHECK (true); + + +-- +-- Name: device_types Allow authenticated to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow authenticated to select" ON public.device_types FOR SELECT TO authenticated USING (true); + + +-- +-- Name: metering_hardware_imports Allow authenticated to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow authenticated to select" ON public.metering_hardware_imports FOR SELECT TO authenticated USING (true); + + +-- +-- Name: pd_action_templates Allow authenticated to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow authenticated to select" ON public.pd_action_templates FOR SELECT TO authenticated USING (true); + + +-- +-- Name: pd_audits Allow authenticated to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow authenticated to select" ON public.pd_audits FOR SELECT TO authenticated USING (true); + + +-- +-- Name: pd_document_templates Allow authenticated to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow authenticated to select" ON public.pd_document_templates FOR SELECT TO authenticated USING (true); + + +-- +-- Name: pd_documents Allow authenticated to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow authenticated to select" ON public.pd_documents FOR SELECT TO authenticated USING (true); + + +-- +-- Name: pd_flow_templates Allow authenticated to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow authenticated to select" ON public.pd_flow_templates FOR SELECT TO authenticated USING (true); + + +-- +-- Name: device_types Allow authenticated to update; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow authenticated to update" ON public.device_types FOR UPDATE TO authenticated USING (true); + + +-- +-- Name: notes Allow authors to update their own; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow authors to update their own" ON public.notes FOR UPDATE TO authenticated USING ((( SELECT auth.uid() AS uid) = ( SELECT account.supabase_id + FROM public.accounts account + WHERE (account.id = notes.author_id)))); + + +-- +-- Name: device_types Allow grafana to read; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow grafana to read" ON public.device_types FOR SELECT TO grafana_readonly USING (true); + + +-- +-- Name: device_logs Allow grafana to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow grafana to select" ON public.device_logs FOR SELECT TO grafana_readonly USING (true); + + +-- +-- Name: devices Allow grafana to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow grafana to select" ON public.devices FOR SELECT TO grafana_readonly USING (true); + + +-- +-- Name: metering_hardware_install_sessions Allow grafana to select all; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow grafana to select all" ON public.metering_hardware_install_sessions FOR SELECT TO grafana_readonly USING (true); + + +-- +-- Name: accounts Allow lenders to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow lenders to select" ON public.accounts FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_lender() AS rls_check_if_lender)); + + +-- +-- Name: customers Allow lenders to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow lenders to select" ON public.customers FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_lender() AS rls_check_if_lender)); + + +-- +-- Name: grids Allow lenders to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow lenders to select" ON public.grids FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_lender() AS rls_check_if_lender)); + + +-- +-- Name: orders Allow lenders to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow lenders to select" ON public.orders FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_lender() AS rls_check_if_lender)); + + +-- +-- Name: device_logs Allow member org to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow member org to select" ON public.device_logs FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + + +-- +-- Name: accounts Allow org member to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org member to select" ON public.accounts FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = organization_id)); + + +-- +-- Name: notes Allow org members to insert; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to insert" ON public.notes FOR INSERT TO authenticated WITH CHECK ((public.rls_get_member_org_id() = rls_organization_id)); + + +-- +-- Name: poles Allow org members to insert; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to insert" ON public.poles FOR INSERT WITH CHECK ((public.rls_get_member_org_id() = rls_organization_id)); + + +-- +-- Name: agents Allow org members to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select" ON public.agents FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + + +-- +-- Name: connection_requested_meters Allow org members to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select" ON public.connection_requested_meters FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + + +-- +-- Name: connections Allow org members to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select" ON public.connections FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + + +-- +-- Name: customers Allow org members to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select" ON public.customers FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + + +-- +-- Name: dcus Allow org members to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select" ON public.dcus FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + + +-- +-- Name: devices Allow org members to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select" ON public.devices FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + + +-- +-- Name: directive_batches Allow org members to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select" ON public.directive_batches FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + + +-- +-- Name: directives Allow org members to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select" ON public.directives FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + + +-- +-- Name: energy_cabins Allow org members to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select" ON public.energy_cabins FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + + +-- +-- Name: grids Allow org members to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select" ON public.grids FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = organization_id)); + + +-- +-- Name: issues Allow org members to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select" ON public.issues FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + + +-- +-- Name: lorawan_directives Allow org members to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select" ON public.lorawan_directives FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + + +-- +-- Name: members Allow org members to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select" ON public.members FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + + +-- +-- Name: meter_commissionings Allow org members to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select" ON public.meter_commissionings FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + + +-- +-- Name: meter_interactions Allow org members to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select" ON public.meter_interactions FOR SELECT TO authenticated USING (true); + + +-- +-- Name: metering_hardware_install_sessions Allow org members to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select" ON public.metering_hardware_install_sessions FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + + +-- +-- Name: mppts Allow org members to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select" ON public.mppts FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + + +-- +-- Name: notes Allow org members to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select" ON public.notes FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + + +-- +-- Name: orders Allow org members to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select" ON public.orders FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + + +-- +-- Name: organizations Allow org members to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select" ON public.organizations FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = id)); + + +-- +-- Name: poles Allow org members to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select" ON public.poles FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + + +-- +-- Name: routers Allow org members to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select" ON public.routers FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + + +-- +-- Name: transactions Allow org members to select; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select" ON public.transactions FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + + +-- +-- Name: wallets Allow org members to select (TEMPORARILY OPEN); Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select (TEMPORARILY OPEN)" ON public.wallets FOR SELECT TO authenticated USING (true); + + +-- +-- Name: meters Allow org members to select own and UNASSIGNED; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to select own and UNASSIGNED" ON public.meters FOR SELECT TO authenticated USING (((rls_organization_id IS NULL) OR (( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id))); + + +-- +-- Name: accounts Allow org members to update; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to update" ON public.accounts FOR UPDATE TO authenticated USING ((public.rls_get_member_org_id() = organization_id)); + + +-- +-- Name: orders Allow org members to update; Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow org members to update" ON public.orders FOR UPDATE TO authenticated USING ((public.rls_get_member_org_id() = rls_organization_id)); + + +-- +-- Name: pd_site_submissions Allow public selects (temporary for sending response to MAKE); Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow public selects (temporary for sending response to MAKE)" ON public.pd_site_submissions FOR SELECT USING (true); + + +-- +-- Name: pd_site_submissions Allow public writes (Sites can be submitted publicly); Type: POLICY; Schema: public; Owner: postgres +-- + +CREATE POLICY "Allow public writes (Sites can be submitted publicly)" ON public.pd_site_submissions FOR INSERT WITH CHECK (true); + + +-- +-- Name: accounts; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.accounts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agents; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.agents ENABLE ROW LEVEL SECURITY; + +-- +-- Name: api_keys; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.api_keys ENABLE ROW LEVEL SECURITY; + +-- +-- Name: audits; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.audits ENABLE ROW LEVEL SECURITY; + +-- +-- Name: autopilot_executions; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.autopilot_executions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: bank_accounts; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.bank_accounts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: banks; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.banks ENABLE ROW LEVEL SECURITY; + +-- +-- Name: connection_requested_meters; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.connection_requested_meters ENABLE ROW LEVEL SECURITY; + +-- +-- Name: connections; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.connections ENABLE ROW LEVEL SECURITY; + +-- +-- Name: customers; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.customers ENABLE ROW LEVEL SECURITY; + +-- +-- Name: dcus; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.dcus ENABLE ROW LEVEL SECURITY; + +-- +-- Name: device_logs; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.device_logs ENABLE ROW LEVEL SECURITY; + +-- +-- Name: device_types; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.device_types ENABLE ROW LEVEL SECURITY; + +-- +-- Name: devices; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.devices ENABLE ROW LEVEL SECURITY; + +-- +-- Name: directive_batch_executions; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.directive_batch_executions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: directive_batches; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.directive_batches ENABLE ROW LEVEL SECURITY; + +-- +-- Name: directive_watchdog_sessions; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.directive_watchdog_sessions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: directives; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.directives ENABLE ROW LEVEL SECURITY; + +-- +-- Name: energy_cabins; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.energy_cabins ENABLE ROW LEVEL SECURITY; + +-- +-- Name: features; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.features ENABLE ROW LEVEL SECURITY; + +-- +-- Name: grids; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.grids ENABLE ROW LEVEL SECURITY; + +-- +-- Name: issues; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.issues ENABLE ROW LEVEL SECURITY; + +-- +-- Name: lorawan_directives; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.lorawan_directives ENABLE ROW LEVEL SECURITY; + +-- +-- Name: member_feature; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.member_feature ENABLE ROW LEVEL SECURITY; + +-- +-- Name: members; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.members ENABLE ROW LEVEL SECURITY; + +-- +-- Name: meter_commissionings; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.meter_commissionings ENABLE ROW LEVEL SECURITY; + +-- +-- Name: meter_credit_transfers; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.meter_credit_transfers ENABLE ROW LEVEL SECURITY; + +-- +-- Name: meter_interactions; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.meter_interactions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: metering_hardware_imports; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.metering_hardware_imports ENABLE ROW LEVEL SECURITY; + +-- +-- Name: metering_hardware_install_sessions; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.metering_hardware_install_sessions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: meters; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.meters ENABLE ROW LEVEL SECURITY; + +-- +-- Name: migrations; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.migrations ENABLE ROW LEVEL SECURITY; + +-- +-- Name: mppts; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.mppts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notes; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.notes ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notification_parameters; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.notification_parameters ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notifications; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.notifications ENABLE ROW LEVEL SECURITY; + +-- +-- Name: orders; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.orders ENABLE ROW LEVEL SECURITY; + +-- +-- Name: organizations; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.organizations ENABLE ROW LEVEL SECURITY; + +-- +-- Name: payouts; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.payouts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: pd_action_templates; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.pd_action_templates ENABLE ROW LEVEL SECURITY; + +-- +-- Name: pd_actions; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.pd_actions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: pd_audits; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.pd_audits ENABLE ROW LEVEL SECURITY; + +-- +-- Name: pd_document_templates; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.pd_document_templates ENABLE ROW LEVEL SECURITY; + +-- +-- Name: pd_documents; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.pd_documents ENABLE ROW LEVEL SECURITY; + +-- +-- Name: pd_flow_templates; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.pd_flow_templates ENABLE ROW LEVEL SECURITY; + +-- +-- Name: pd_flows; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.pd_flows ENABLE ROW LEVEL SECURITY; + +-- +-- Name: pd_section_templates; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.pd_section_templates ENABLE ROW LEVEL SECURITY; + +-- +-- Name: pd_sections; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.pd_sections ENABLE ROW LEVEL SECURITY; + +-- +-- Name: pd_site_submissions; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.pd_site_submissions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: pd_sites; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.pd_sites ENABLE ROW LEVEL SECURITY; + +-- +-- Name: poles; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.poles ENABLE ROW LEVEL SECURITY; + +-- +-- Name: routers; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.routers ENABLE ROW LEVEL SECURITY; + +-- +-- Name: solcast_cache; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.solcast_cache ENABLE ROW LEVEL SECURITY; + +-- +-- Name: transactions; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.transactions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: ussd_session_hops; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.ussd_session_hops ENABLE ROW LEVEL SECURITY; + +-- +-- Name: ussd_sessions; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.ussd_sessions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: wallets; Type: ROW SECURITY; Schema: public; Owner: postgres +-- + +ALTER TABLE public.wallets ENABLE ROW LEVEL SECURITY; + +-- +-- Name: SCHEMA public; Type: ACL; Schema: -; Owner: pg_database_owner +-- + +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 USAGE ON SCHEMA public TO grafana_readonly; +GRANT USAGE ON SCHEMA public TO make_readonly; +GRANT USAGE ON SCHEMA public TO snaplet_readonly_2; + + +-- +-- Name: FUNCTION append_rls_organization_id_by_account_id(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_account_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_account_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_account_id() TO service_role; + + +-- +-- Name: FUNCTION append_rls_organization_id_by_connection_id(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_connection_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_connection_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_connection_id() TO service_role; + + +-- +-- Name: FUNCTION append_rls_organization_id_by_customer_id(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_customer_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_customer_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_customer_id() TO service_role; + + +-- +-- Name: FUNCTION append_rls_organization_id_by_customer_id_or_agent_id_or_connec(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_customer_id_or_agent_id_or_connec() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_customer_id_or_agent_id_or_connec() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_customer_id_or_agent_id_or_connec() TO service_role; + + +-- +-- Name: FUNCTION append_rls_organization_id_by_dcu_id_or_meter_id(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_dcu_id_or_meter_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_dcu_id_or_meter_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_dcu_id_or_meter_id() TO service_role; + + +-- +-- Name: FUNCTION append_rls_organization_id_by_device_id(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_device_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_device_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_device_id() TO service_role; + + +-- +-- Name: FUNCTION append_rls_organization_id_by_directive_batch_id(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_directive_batch_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_directive_batch_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_directive_batch_id() TO service_role; + + +-- +-- Name: FUNCTION append_rls_organization_id_by_grid_id(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_grid_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_grid_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_grid_id() TO service_role; + + +-- +-- Name: FUNCTION append_rls_organization_id_by_historical_grid_id(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_historical_grid_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_historical_grid_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_historical_grid_id() TO service_role; + + +-- +-- Name: FUNCTION append_rls_organization_id_by_meter_id(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_meter_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_meter_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_meter_id() TO service_role; + + +-- +-- Name: FUNCTION append_rls_organization_id_by_metering_hardware_install_session(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_metering_hardware_install_session() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_metering_hardware_install_session() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_metering_hardware_install_session() TO service_role; + + +-- +-- Name: FUNCTION append_rls_organization_id_by_order_id(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_order_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_order_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_order_id() TO service_role; + + +-- +-- Name: FUNCTION append_rls_organization_id_by_receiver_meter_id(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_receiver_meter_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_receiver_meter_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_receiver_meter_id() TO service_role; + + +-- +-- Name: FUNCTION find_energy_topup_revenue(grid_id integer, start_date timestamp with time zone, end_date timestamp with time zone); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.find_energy_topup_revenue(grid_id integer, start_date timestamp with time zone, end_date timestamp with time zone) TO anon; +GRANT ALL ON FUNCTION public.find_energy_topup_revenue(grid_id integer, start_date timestamp with time zone, end_date timestamp with time zone) TO authenticated; +GRANT ALL ON FUNCTION public.find_energy_topup_revenue(grid_id integer, start_date timestamp with time zone, end_date timestamp with time zone) TO service_role; + + +-- +-- Name: FUNCTION find_top_spenders(grid_id integer, limit_count integer, start_date timestamp with time zone, end_date timestamp with time zone); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.find_top_spenders(grid_id integer, limit_count integer, start_date timestamp with time zone, end_date timestamp with time zone) TO anon; +GRANT ALL ON FUNCTION public.find_top_spenders(grid_id integer, limit_count integer, start_date timestamp with time zone, end_date timestamp with time zone) TO authenticated; +GRANT ALL ON FUNCTION public.find_top_spenders(grid_id integer, limit_count integer, start_date timestamp with time zone, end_date timestamp with time zone) TO service_role; + + +-- +-- Name: FUNCTION get_grid_status(grid_id integer); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.get_grid_status(grid_id integer) TO anon; +GRANT ALL ON FUNCTION public.get_grid_status(grid_id integer) TO authenticated; +GRANT ALL ON FUNCTION public.get_grid_status(grid_id integer) TO service_role; + + +-- +-- Name: FUNCTION handle_new_user(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.handle_new_user() TO anon; +GRANT ALL ON FUNCTION public.handle_new_user() TO authenticated; +GRANT ALL ON FUNCTION public.handle_new_user() TO service_role; + + +-- +-- Name: FUNCTION handle_update_user(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.handle_update_user() TO anon; +GRANT ALL ON FUNCTION public.handle_update_user() TO authenticated; +GRANT ALL ON FUNCTION public.handle_update_user() TO service_role; + + +-- +-- Name: FUNCTION lock_next_order(uuid uuid); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.lock_next_order(uuid uuid) TO anon; +GRANT ALL ON FUNCTION public.lock_next_order(uuid uuid) TO authenticated; +GRANT ALL ON FUNCTION public.lock_next_order(uuid uuid) TO service_role; + + +-- +-- Name: FUNCTION lock_next_order_and_wallets(uuid uuid); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.lock_next_order_and_wallets(uuid uuid) TO anon; +GRANT ALL ON FUNCTION public.lock_next_order_and_wallets(uuid uuid) TO authenticated; +GRANT ALL ON FUNCTION public.lock_next_order_and_wallets(uuid uuid) TO service_role; + + +-- +-- Name: FUNCTION lock_next_pd_action(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.lock_next_pd_action() TO anon; +GRANT ALL ON FUNCTION public.lock_next_pd_action() TO authenticated; +GRANT ALL ON FUNCTION public.lock_next_pd_action() TO service_role; + + +-- +-- Name: FUNCTION notify_make_about_is_fs_on_updated(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.notify_make_about_is_fs_on_updated() TO anon; +GRANT ALL ON FUNCTION public.notify_make_about_is_fs_on_updated() TO authenticated; +GRANT ALL ON FUNCTION public.notify_make_about_is_fs_on_updated() TO service_role; + + +-- +-- Name: FUNCTION notify_make_about_is_hps_on_updated(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.notify_make_about_is_hps_on_updated() TO anon; +GRANT ALL ON FUNCTION public.notify_make_about_is_hps_on_updated() TO authenticated; +GRANT ALL ON FUNCTION public.notify_make_about_is_hps_on_updated() TO service_role; + + +-- +-- Name: FUNCTION notify_make_about_kwh_tariff_essential_service_updated(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.notify_make_about_kwh_tariff_essential_service_updated() TO anon; +GRANT ALL ON FUNCTION public.notify_make_about_kwh_tariff_essential_service_updated() TO authenticated; +GRANT ALL ON FUNCTION public.notify_make_about_kwh_tariff_essential_service_updated() TO service_role; + + +-- +-- Name: FUNCTION rls_check_if_lender(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.rls_check_if_lender() TO anon; +GRANT ALL ON FUNCTION public.rls_check_if_lender() TO authenticated; +GRANT ALL ON FUNCTION public.rls_check_if_lender() TO service_role; + + +-- +-- Name: FUNCTION rls_check_if_nxt_member(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.rls_check_if_nxt_member() TO anon; +GRANT ALL ON FUNCTION public.rls_check_if_nxt_member() TO authenticated; +GRANT ALL ON FUNCTION public.rls_check_if_nxt_member() TO service_role; + + +-- +-- Name: FUNCTION rls_get_member_org_id(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.rls_get_member_org_id() TO anon; +GRANT ALL ON FUNCTION public.rls_get_member_org_id() TO authenticated; +GRANT ALL ON FUNCTION public.rls_get_member_org_id() TO service_role; + + +-- +-- Name: TABLE accounts; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.accounts TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.accounts TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.accounts TO service_role; +GRANT SELECT ON TABLE public.accounts TO grafana_readonly; +GRANT SELECT ON TABLE public.accounts TO make_readonly; +GRANT SELECT ON TABLE public.accounts TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE accounts_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.accounts_id_seq TO anon; +GRANT ALL ON SEQUENCE public.accounts_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.accounts_id_seq TO service_role; + + +-- +-- Name: TABLE agents; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.agents TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.agents TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.agents TO service_role; +GRANT SELECT ON TABLE public.agents TO grafana_readonly; +GRANT SELECT ON TABLE public.agents TO make_readonly; +GRANT SELECT ON TABLE public.agents TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE agents_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.agents_id_seq TO anon; +GRANT ALL ON SEQUENCE public.agents_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.agents_id_seq TO service_role; + + +-- +-- Name: TABLE agents_with_account; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.agents_with_account TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.agents_with_account TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.agents_with_account TO service_role; +GRANT SELECT ON TABLE public.agents_with_account TO grafana_readonly; +GRANT SELECT ON TABLE public.agents_with_account TO make_readonly; +GRANT SELECT ON TABLE public.agents_with_account TO snaplet_readonly_2; + + +-- +-- Name: TABLE api_keys; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.api_keys TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.api_keys TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.api_keys TO service_role; +GRANT SELECT ON TABLE public.api_keys TO grafana_readonly; +GRANT SELECT ON TABLE public.api_keys TO make_readonly; +GRANT SELECT ON TABLE public.api_keys TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE api_keys_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.api_keys_id_seq TO anon; +GRANT ALL ON SEQUENCE public.api_keys_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.api_keys_id_seq TO service_role; + + +-- +-- Name: TABLE audits; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.audits TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.audits TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.audits TO service_role; +GRANT SELECT ON TABLE public.audits TO grafana_readonly; +GRANT SELECT ON TABLE public.audits TO make_readonly; +GRANT SELECT ON TABLE public.audits TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE audits_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.audits_id_seq TO anon; +GRANT ALL ON SEQUENCE public.audits_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.audits_id_seq TO service_role; + + +-- +-- Name: TABLE autopilot_executions; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.autopilot_executions TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.autopilot_executions TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.autopilot_executions TO service_role; +GRANT SELECT ON TABLE public.autopilot_executions TO grafana_readonly; +GRANT SELECT ON TABLE public.autopilot_executions TO make_readonly; +GRANT SELECT ON TABLE public.autopilot_executions TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE autopilot_executions_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.autopilot_executions_id_seq TO anon; +GRANT ALL ON SEQUENCE public.autopilot_executions_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.autopilot_executions_id_seq TO service_role; + + +-- +-- Name: TABLE bank_accounts; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.bank_accounts TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.bank_accounts TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.bank_accounts TO service_role; +GRANT SELECT ON TABLE public.bank_accounts TO grafana_readonly; +GRANT SELECT ON TABLE public.bank_accounts TO make_readonly; +GRANT SELECT ON TABLE public.bank_accounts TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE bank_accounts_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.bank_accounts_id_seq TO anon; +GRANT ALL ON SEQUENCE public.bank_accounts_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.bank_accounts_id_seq TO service_role; + + +-- +-- Name: TABLE banks; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.banks TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.banks TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.banks TO service_role; +GRANT SELECT ON TABLE public.banks TO grafana_readonly; +GRANT SELECT ON TABLE public.banks TO make_readonly; +GRANT SELECT ON TABLE public.banks TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE banks_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.banks_id_seq TO anon; +GRANT ALL ON SEQUENCE public.banks_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.banks_id_seq TO service_role; + + +-- +-- Name: TABLE dcus; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.dcus TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.dcus TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.dcus TO service_role; +GRANT SELECT ON TABLE public.dcus TO grafana_readonly; +GRANT SELECT ON TABLE public.dcus TO make_readonly; +GRANT SELECT ON TABLE public.dcus TO snaplet_readonly_2; + + +-- +-- Name: TABLE directives; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.directives TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.directives TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.directives TO service_role; +GRANT SELECT ON TABLE public.directives TO grafana_readonly; +GRANT SELECT ON TABLE public.directives TO make_readonly; +GRANT SELECT ON TABLE public.directives TO snaplet_readonly_2; + + +-- +-- Name: TABLE lorawan_directives; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.lorawan_directives TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.lorawan_directives TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.lorawan_directives TO service_role; +GRANT SELECT ON TABLE public.lorawan_directives TO grafana_readonly; +GRANT SELECT ON TABLE public.lorawan_directives TO make_readonly; +GRANT SELECT ON TABLE public.lorawan_directives TO snaplet_readonly_2; + + +-- +-- Name: TABLE meters; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meters TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meters TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meters TO service_role; +GRANT SELECT ON TABLE public.meters TO grafana_readonly; +GRANT SELECT ON TABLE public.meters TO make_readonly; +GRANT SELECT ON TABLE public.meters TO snaplet_readonly_2; + + +-- +-- Name: TABLE poles; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.poles TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.poles TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.poles TO service_role; +GRANT SELECT ON TABLE public.poles TO grafana_readonly; +GRANT SELECT ON TABLE public.poles TO make_readonly; +GRANT SELECT ON TABLE public.poles TO snaplet_readonly_2; + + +-- +-- Name: TABLE batch_commands; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.batch_commands TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.batch_commands TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.batch_commands TO service_role; +GRANT SELECT ON TABLE public.batch_commands TO grafana_readonly; +GRANT SELECT ON TABLE public.batch_commands TO make_readonly; +GRANT SELECT ON TABLE public.batch_commands TO snaplet_readonly_2; + + +-- +-- Name: TABLE connection_requested_meters; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.connection_requested_meters TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.connection_requested_meters TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.connection_requested_meters TO service_role; +GRANT SELECT ON TABLE public.connection_requested_meters TO grafana_readonly; +GRANT SELECT ON TABLE public.connection_requested_meters TO make_readonly; +GRANT SELECT ON TABLE public.connection_requested_meters TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE connection_requested_meters_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.connection_requested_meters_id_seq TO anon; +GRANT ALL ON SEQUENCE public.connection_requested_meters_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.connection_requested_meters_id_seq TO service_role; + + +-- +-- Name: TABLE connections; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.connections TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.connections TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.connections TO service_role; +GRANT SELECT ON TABLE public.connections TO grafana_readonly; +GRANT SELECT ON TABLE public.connections TO make_readonly; +GRANT SELECT ON TABLE public.connections TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE connections_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.connections_id_seq TO anon; +GRANT ALL ON SEQUENCE public.connections_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.connections_id_seq TO service_role; + + +-- +-- Name: TABLE customers; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.customers TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.customers TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.customers TO service_role; +GRANT SELECT ON TABLE public.customers TO grafana_readonly; +GRANT SELECT ON TABLE public.customers TO make_readonly; +GRANT SELECT ON TABLE public.customers TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE customers_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.customers_id_seq TO anon; +GRANT ALL ON SEQUENCE public.customers_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.customers_id_seq TO service_role; + + +-- +-- Name: TABLE customers_with_account; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.customers_with_account TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.customers_with_account TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.customers_with_account TO service_role; +GRANT SELECT ON TABLE public.customers_with_account TO grafana_readonly; +GRANT SELECT ON TABLE public.customers_with_account TO make_readonly; +GRANT SELECT ON TABLE public.customers_with_account TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE dcus_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.dcus_id_seq TO anon; +GRANT ALL ON SEQUENCE public.dcus_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.dcus_id_seq TO service_role; + + +-- +-- Name: TABLE device_logs; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.device_logs TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.device_logs TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.device_logs TO service_role; +GRANT SELECT ON TABLE public.device_logs TO grafana_readonly; +GRANT SELECT ON TABLE public.device_logs TO make_readonly; +GRANT SELECT ON TABLE public.device_logs TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE device_logs_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.device_logs_id_seq TO anon; +GRANT ALL ON SEQUENCE public.device_logs_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.device_logs_id_seq TO service_role; + + +-- +-- Name: TABLE device_types; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.device_types TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.device_types TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.device_types TO service_role; +GRANT SELECT ON TABLE public.device_types TO grafana_readonly; +GRANT SELECT ON TABLE public.device_types TO make_readonly; +GRANT SELECT ON TABLE public.device_types TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE device_types_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.device_types_id_seq TO anon; +GRANT ALL ON SEQUENCE public.device_types_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.device_types_id_seq TO service_role; + + +-- +-- Name: TABLE devices; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.devices TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.devices TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.devices TO service_role; +GRANT SELECT ON TABLE public.devices TO grafana_readonly; +GRANT SELECT ON TABLE public.devices TO make_readonly; +GRANT SELECT ON TABLE public.devices TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE devices_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.devices_id_seq TO anon; +GRANT ALL ON SEQUENCE public.devices_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.devices_id_seq TO service_role; + + +-- +-- Name: TABLE directive_batch_executions; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.directive_batch_executions TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.directive_batch_executions TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.directive_batch_executions TO service_role; +GRANT SELECT ON TABLE public.directive_batch_executions TO grafana_readonly; +GRANT SELECT ON TABLE public.directive_batch_executions TO make_readonly; +GRANT SELECT ON TABLE public.directive_batch_executions TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE directive_batch_executions_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.directive_batch_executions_id_seq TO anon; +GRANT ALL ON SEQUENCE public.directive_batch_executions_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.directive_batch_executions_id_seq TO service_role; + + +-- +-- Name: TABLE directive_batches; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.directive_batches TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.directive_batches TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.directive_batches TO service_role; +GRANT SELECT ON TABLE public.directive_batches TO grafana_readonly; +GRANT SELECT ON TABLE public.directive_batches TO make_readonly; +GRANT SELECT ON TABLE public.directive_batches TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE directive_batches_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.directive_batches_id_seq TO anon; +GRANT ALL ON SEQUENCE public.directive_batches_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.directive_batches_id_seq TO service_role; + + +-- +-- Name: TABLE directive_watchdog_sessions; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.directive_watchdog_sessions TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.directive_watchdog_sessions TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.directive_watchdog_sessions TO service_role; +GRANT SELECT ON TABLE public.directive_watchdog_sessions TO grafana_readonly; +GRANT SELECT ON TABLE public.directive_watchdog_sessions TO make_readonly; +GRANT SELECT ON TABLE public.directive_watchdog_sessions TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE directive_watchdog_sessions_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.directive_watchdog_sessions_id_seq TO anon; +GRANT ALL ON SEQUENCE public.directive_watchdog_sessions_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.directive_watchdog_sessions_id_seq TO service_role; + + +-- +-- Name: SEQUENCE directives_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.directives_id_seq TO anon; +GRANT ALL ON SEQUENCE public.directives_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.directives_id_seq TO service_role; + + +-- +-- Name: TABLE energy_cabins; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.energy_cabins TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.energy_cabins TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.energy_cabins TO service_role; +GRANT SELECT ON TABLE public.energy_cabins TO grafana_readonly; +GRANT SELECT ON TABLE public.energy_cabins TO make_readonly; +GRANT SELECT ON TABLE public.energy_cabins TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE energy_cabins_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.energy_cabins_id_seq TO anon; +GRANT ALL ON SEQUENCE public.energy_cabins_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.energy_cabins_id_seq TO service_role; + + +-- +-- Name: TABLE features; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.features TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.features TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.features TO service_role; +GRANT SELECT ON TABLE public.features TO grafana_readonly; +GRANT SELECT ON TABLE public.features TO make_readonly; +GRANT SELECT ON TABLE public.features TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE features_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.features_id_seq TO anon; +GRANT ALL ON SEQUENCE public.features_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.features_id_seq TO service_role; + + +-- +-- Name: TABLE grids; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.grids TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.grids TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.grids TO service_role; +GRANT SELECT ON TABLE public.grids TO grafana_readonly; +GRANT SELECT ON TABLE public.grids TO make_readonly; +GRANT SELECT ON TABLE public.grids TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE grids_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.grids_id_seq TO anon; +GRANT ALL ON SEQUENCE public.grids_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.grids_id_seq TO service_role; + + +-- +-- Name: TABLE issues; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.issues TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.issues TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.issues TO service_role; +GRANT SELECT ON TABLE public.issues TO grafana_readonly; +GRANT SELECT ON TABLE public.issues TO make_readonly; +GRANT SELECT ON TABLE public.issues TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE issues_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.issues_id_seq TO anon; +GRANT ALL ON SEQUENCE public.issues_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.issues_id_seq TO service_role; + + +-- +-- Name: SEQUENCE lorawan_directives_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.lorawan_directives_id_seq TO anon; +GRANT ALL ON SEQUENCE public.lorawan_directives_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.lorawan_directives_id_seq TO service_role; + + +-- +-- Name: TABLE member_feature; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.member_feature TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.member_feature TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.member_feature TO service_role; +GRANT SELECT ON TABLE public.member_feature TO grafana_readonly; +GRANT SELECT ON TABLE public.member_feature TO make_readonly; +GRANT SELECT ON TABLE public.member_feature TO snaplet_readonly_2; + + +-- +-- Name: TABLE members; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.members TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.members TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.members TO service_role; +GRANT SELECT ON TABLE public.members TO grafana_readonly; +GRANT SELECT ON TABLE public.members TO make_readonly; +GRANT SELECT ON TABLE public.members TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE members_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.members_id_seq TO anon; +GRANT ALL ON SEQUENCE public.members_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.members_id_seq TO service_role; + + +-- +-- Name: TABLE meter_commissionings; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_commissionings TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_commissionings TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_commissionings TO service_role; +GRANT SELECT ON TABLE public.meter_commissionings TO grafana_readonly; +GRANT SELECT ON TABLE public.meter_commissionings TO make_readonly; +GRANT SELECT ON TABLE public.meter_commissionings TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE meter_commissionings_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.meter_commissionings_id_seq TO anon; +GRANT ALL ON SEQUENCE public.meter_commissionings_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.meter_commissionings_id_seq TO service_role; + + +-- +-- Name: TABLE meter_credit_transfers; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_credit_transfers TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_credit_transfers TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_credit_transfers TO service_role; +GRANT SELECT ON TABLE public.meter_credit_transfers TO grafana_readonly; +GRANT SELECT ON TABLE public.meter_credit_transfers TO make_readonly; +GRANT SELECT ON TABLE public.meter_credit_transfers TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE meter_credit_transfers_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.meter_credit_transfers_id_seq TO anon; +GRANT ALL ON SEQUENCE public.meter_credit_transfers_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.meter_credit_transfers_id_seq TO service_role; + + +-- +-- Name: TABLE meter_interactions; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_interactions TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_interactions TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_interactions TO service_role; +GRANT SELECT ON TABLE public.meter_interactions TO grafana_readonly; +GRANT SELECT ON TABLE public.meter_interactions TO make_readonly; +GRANT SELECT ON TABLE public.meter_interactions TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE meter_interactions_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.meter_interactions_id_seq TO anon; +GRANT ALL ON SEQUENCE public.meter_interactions_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.meter_interactions_id_seq TO service_role; + + +-- +-- Name: TABLE metering_hardware_imports; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.metering_hardware_imports TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.metering_hardware_imports TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.metering_hardware_imports TO service_role; +GRANT SELECT ON TABLE public.metering_hardware_imports TO grafana_readonly; +GRANT SELECT ON TABLE public.metering_hardware_imports TO make_readonly; +GRANT SELECT ON TABLE public.metering_hardware_imports TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE metering_hardware_imports_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.metering_hardware_imports_id_seq TO anon; +GRANT ALL ON SEQUENCE public.metering_hardware_imports_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.metering_hardware_imports_id_seq TO service_role; + + +-- +-- Name: TABLE metering_hardware_install_sessions; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.metering_hardware_install_sessions TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.metering_hardware_install_sessions TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.metering_hardware_install_sessions TO service_role; +GRANT SELECT ON TABLE public.metering_hardware_install_sessions TO grafana_readonly; +GRANT SELECT ON TABLE public.metering_hardware_install_sessions TO make_readonly; +GRANT SELECT ON TABLE public.metering_hardware_install_sessions TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE metering_hardware_install_sessions_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.metering_hardware_install_sessions_id_seq TO anon; +GRANT ALL ON SEQUENCE public.metering_hardware_install_sessions_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.metering_hardware_install_sessions_id_seq TO service_role; + + +-- +-- Name: SEQUENCE meters_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.meters_id_seq TO anon; +GRANT ALL ON SEQUENCE public.meters_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.meters_id_seq TO service_role; + + +-- +-- Name: TABLE meters_with_account_and_statuses; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meters_with_account_and_statuses TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meters_with_account_and_statuses TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meters_with_account_and_statuses TO service_role; +GRANT SELECT ON TABLE public.meters_with_account_and_statuses TO grafana_readonly; +GRANT SELECT ON TABLE public.meters_with_account_and_statuses TO make_readonly; +GRANT SELECT ON TABLE public.meters_with_account_and_statuses TO snaplet_readonly_2; + + +-- +-- Name: TABLE migrations; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.migrations TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.migrations TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.migrations TO service_role; +GRANT SELECT ON TABLE public.migrations TO grafana_readonly; +GRANT SELECT ON TABLE public.migrations TO make_readonly; +GRANT SELECT ON TABLE public.migrations TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE migrations_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.migrations_id_seq TO anon; +GRANT ALL ON SEQUENCE public.migrations_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.migrations_id_seq TO service_role; + + +-- +-- Name: TABLE mppts; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.mppts TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.mppts TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.mppts TO service_role; +GRANT SELECT ON TABLE public.mppts TO grafana_readonly; +GRANT SELECT ON TABLE public.mppts TO make_readonly; +GRANT SELECT ON TABLE public.mppts TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE mppts_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.mppts_id_seq TO anon; +GRANT ALL ON SEQUENCE public.mppts_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.mppts_id_seq TO service_role; + + +-- +-- Name: TABLE notes; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notes TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notes TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notes TO service_role; +GRANT SELECT ON TABLE public.notes TO grafana_readonly; +GRANT SELECT ON TABLE public.notes TO make_readonly; +GRANT SELECT ON TABLE public.notes TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE notes_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.notes_id_seq TO anon; +GRANT ALL ON SEQUENCE public.notes_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.notes_id_seq TO service_role; + + +-- +-- Name: TABLE notification_parameters; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notification_parameters TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notification_parameters TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notification_parameters TO service_role; +GRANT SELECT ON TABLE public.notification_parameters TO grafana_readonly; +GRANT SELECT ON TABLE public.notification_parameters TO make_readonly; +GRANT SELECT ON TABLE public.notification_parameters TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE notification_parameters_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.notification_parameters_id_seq TO anon; +GRANT ALL ON SEQUENCE public.notification_parameters_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.notification_parameters_id_seq TO service_role; + + +-- +-- Name: TABLE notifications; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notifications TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notifications TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notifications TO service_role; +GRANT SELECT ON TABLE public.notifications TO grafana_readonly; +GRANT SELECT ON TABLE public.notifications TO make_readonly; +GRANT SELECT ON TABLE public.notifications TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE notifications_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.notifications_id_seq TO anon; +GRANT ALL ON SEQUENCE public.notifications_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.notifications_id_seq TO service_role; + + +-- +-- Name: TABLE orders; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.orders TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.orders TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.orders TO service_role; +GRANT SELECT ON TABLE public.orders TO grafana_readonly; +GRANT SELECT ON TABLE public.orders TO make_readonly; +GRANT SELECT ON TABLE public.orders TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE orders_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.orders_id_seq TO anon; +GRANT ALL ON SEQUENCE public.orders_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.orders_id_seq TO service_role; + + +-- +-- Name: TABLE organizations; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.organizations TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.organizations TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.organizations TO service_role; +GRANT SELECT ON TABLE public.organizations TO grafana_readonly; +GRANT SELECT ON TABLE public.organizations TO make_readonly; +GRANT SELECT ON TABLE public.organizations TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE organizations_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.organizations_id_seq TO anon; +GRANT ALL ON SEQUENCE public.organizations_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.organizations_id_seq TO service_role; + + +-- +-- Name: TABLE payouts; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.payouts TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.payouts TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.payouts TO service_role; +GRANT SELECT ON TABLE public.payouts TO grafana_readonly; +GRANT SELECT ON TABLE public.payouts TO make_readonly; +GRANT SELECT ON TABLE public.payouts TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE payouts_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.payouts_id_seq TO anon; +GRANT ALL ON SEQUENCE public.payouts_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.payouts_id_seq TO service_role; + + +-- +-- Name: TABLE pd_action_templates; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_action_templates TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_action_templates TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_action_templates TO service_role; +GRANT SELECT ON TABLE public.pd_action_templates TO grafana_readonly; +GRANT SELECT ON TABLE public.pd_action_templates TO make_readonly; +GRANT SELECT ON TABLE public.pd_action_templates TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE pd_action_templates_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.pd_action_templates_id_seq TO anon; +GRANT ALL ON SEQUENCE public.pd_action_templates_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.pd_action_templates_id_seq TO service_role; + + +-- +-- Name: TABLE pd_actions; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_actions TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_actions TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_actions TO service_role; +GRANT SELECT ON TABLE public.pd_actions TO grafana_readonly; +GRANT SELECT ON TABLE public.pd_actions TO make_readonly; +GRANT SELECT ON TABLE public.pd_actions TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE pd_actions_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.pd_actions_id_seq TO anon; +GRANT ALL ON SEQUENCE public.pd_actions_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.pd_actions_id_seq TO service_role; + + +-- +-- Name: TABLE pd_audits; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_audits TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_audits TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_audits TO service_role; +GRANT SELECT ON TABLE public.pd_audits TO grafana_readonly; +GRANT SELECT ON TABLE public.pd_audits TO make_readonly; +GRANT SELECT ON TABLE public.pd_audits TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE pd_audits_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.pd_audits_id_seq TO anon; +GRANT ALL ON SEQUENCE public.pd_audits_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.pd_audits_id_seq TO service_role; + + +-- +-- Name: TABLE pd_document_templates; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_document_templates TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_document_templates TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_document_templates TO service_role; +GRANT SELECT ON TABLE public.pd_document_templates TO grafana_readonly; +GRANT SELECT ON TABLE public.pd_document_templates TO make_readonly; +GRANT SELECT ON TABLE public.pd_document_templates TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE pd_document_templates_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.pd_document_templates_id_seq TO anon; +GRANT ALL ON SEQUENCE public.pd_document_templates_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.pd_document_templates_id_seq TO service_role; + + +-- +-- Name: TABLE pd_documents; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_documents TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_documents TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_documents TO service_role; +GRANT SELECT ON TABLE public.pd_documents TO grafana_readonly; +GRANT SELECT ON TABLE public.pd_documents TO make_readonly; +GRANT SELECT ON TABLE public.pd_documents TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE pd_documents_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.pd_documents_id_seq TO anon; +GRANT ALL ON SEQUENCE public.pd_documents_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.pd_documents_id_seq TO service_role; + + +-- +-- Name: TABLE pd_flow_templates; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_flow_templates TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_flow_templates TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_flow_templates TO service_role; +GRANT SELECT ON TABLE public.pd_flow_templates TO grafana_readonly; +GRANT SELECT ON TABLE public.pd_flow_templates TO make_readonly; +GRANT SELECT ON TABLE public.pd_flow_templates TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE pd_flow_templates_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.pd_flow_templates_id_seq TO anon; +GRANT ALL ON SEQUENCE public.pd_flow_templates_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.pd_flow_templates_id_seq TO service_role; + + +-- +-- Name: TABLE pd_flows; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_flows TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_flows TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_flows TO service_role; +GRANT SELECT ON TABLE public.pd_flows TO grafana_readonly; +GRANT SELECT ON TABLE public.pd_flows TO make_readonly; +GRANT SELECT ON TABLE public.pd_flows TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE pd_flows_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.pd_flows_id_seq TO anon; +GRANT ALL ON SEQUENCE public.pd_flows_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.pd_flows_id_seq TO service_role; + + +-- +-- Name: TABLE pd_section_templates; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_section_templates TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_section_templates TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_section_templates TO service_role; +GRANT SELECT ON TABLE public.pd_section_templates TO grafana_readonly; +GRANT SELECT ON TABLE public.pd_section_templates TO make_readonly; +GRANT SELECT ON TABLE public.pd_section_templates TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE pd_section_templates_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.pd_section_templates_id_seq TO anon; +GRANT ALL ON SEQUENCE public.pd_section_templates_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.pd_section_templates_id_seq TO service_role; + + +-- +-- Name: TABLE pd_sections; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_sections TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_sections TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_sections TO service_role; +GRANT SELECT ON TABLE public.pd_sections TO grafana_readonly; +GRANT SELECT ON TABLE public.pd_sections TO make_readonly; +GRANT SELECT ON TABLE public.pd_sections TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE pd_sections_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.pd_sections_id_seq TO anon; +GRANT ALL ON SEQUENCE public.pd_sections_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.pd_sections_id_seq TO service_role; + + +-- +-- Name: TABLE pd_site_submissions; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_site_submissions TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_site_submissions TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_site_submissions TO service_role; +GRANT SELECT ON TABLE public.pd_site_submissions TO grafana_readonly; +GRANT SELECT ON TABLE public.pd_site_submissions TO make_readonly; +GRANT SELECT ON TABLE public.pd_site_submissions TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE pd_site_submissions_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.pd_site_submissions_id_seq TO anon; +GRANT ALL ON SEQUENCE public.pd_site_submissions_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.pd_site_submissions_id_seq TO service_role; + + +-- +-- Name: TABLE pd_sites; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_sites TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_sites TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_sites TO service_role; +GRANT SELECT ON TABLE public.pd_sites TO grafana_readonly; +GRANT SELECT ON TABLE public.pd_sites TO make_readonly; +GRANT SELECT ON TABLE public.pd_sites TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE pd_sites_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.pd_sites_id_seq TO anon; +GRANT ALL ON SEQUENCE public.pd_sites_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.pd_sites_id_seq TO service_role; + + +-- +-- Name: SEQUENCE poles_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.poles_id_seq TO anon; +GRANT ALL ON SEQUENCE public.poles_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.poles_id_seq TO service_role; + + +-- +-- Name: TABLE routers; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.routers TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.routers TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.routers TO service_role; +GRANT SELECT ON TABLE public.routers TO grafana_readonly; +GRANT SELECT ON TABLE public.routers TO make_readonly; +GRANT SELECT ON TABLE public.routers TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE routers_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.routers_id_seq TO anon; +GRANT ALL ON SEQUENCE public.routers_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.routers_id_seq TO service_role; + + +-- +-- Name: TABLE solcast_cache; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.solcast_cache TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.solcast_cache TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.solcast_cache TO service_role; +GRANT SELECT ON TABLE public.solcast_cache TO grafana_readonly; +GRANT SELECT ON TABLE public.solcast_cache TO make_readonly; +GRANT SELECT ON TABLE public.solcast_cache TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE solcast_cache_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.solcast_cache_id_seq TO anon; +GRANT ALL ON SEQUENCE public.solcast_cache_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.solcast_cache_id_seq TO service_role; + + +-- +-- Name: TABLE transactions; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.transactions TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.transactions TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.transactions TO service_role; +GRANT SELECT ON TABLE public.transactions TO grafana_readonly; +GRANT SELECT ON TABLE public.transactions TO make_readonly; +GRANT SELECT ON TABLE public.transactions TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE transactions_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.transactions_id_seq TO anon; +GRANT ALL ON SEQUENCE public.transactions_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.transactions_id_seq TO service_role; + + +-- +-- Name: TABLE ussd_session_hops; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.ussd_session_hops TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.ussd_session_hops TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.ussd_session_hops TO service_role; +GRANT SELECT ON TABLE public.ussd_session_hops TO grafana_readonly; +GRANT SELECT ON TABLE public.ussd_session_hops TO make_readonly; +GRANT SELECT ON TABLE public.ussd_session_hops TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE ussd_session_hops_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.ussd_session_hops_id_seq TO anon; +GRANT ALL ON SEQUENCE public.ussd_session_hops_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.ussd_session_hops_id_seq TO service_role; + + +-- +-- Name: TABLE ussd_sessions; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.ussd_sessions TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.ussd_sessions TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.ussd_sessions TO service_role; +GRANT SELECT ON TABLE public.ussd_sessions TO grafana_readonly; +GRANT SELECT ON TABLE public.ussd_sessions TO make_readonly; +GRANT SELECT ON TABLE public.ussd_sessions TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE ussd_sessions_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.ussd_sessions_id_seq TO anon; +GRANT ALL ON SEQUENCE public.ussd_sessions_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.ussd_sessions_id_seq TO service_role; + + +-- +-- Name: TABLE wallets; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.wallets TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.wallets TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.wallets TO service_role; +GRANT SELECT ON TABLE public.wallets TO grafana_readonly; +GRANT SELECT ON TABLE public.wallets TO make_readonly; +GRANT SELECT ON TABLE public.wallets TO snaplet_readonly_2; + + +-- +-- Name: SEQUENCE wallets_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.wallets_id_seq TO anon; +GRANT ALL ON SEQUENCE public.wallets_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.wallets_id_seq TO service_role; + + +-- +-- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: public; Owner: postgres +-- + +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; + + +-- +-- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: public; Owner: supabase_admin +-- + +ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres; +ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO anon; +ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated; +ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role; + + +-- +-- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: public; Owner: postgres +-- + +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; + + +-- +-- Name: DEFAULT PRIVILEGES FOR FUNCTIONS; Type: DEFAULT ACL; Schema: public; Owner: supabase_admin +-- + +ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres; +ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO anon; +ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated; +ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role; + + +-- +-- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: public; Owner: postgres +-- + +ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLES TO postgres; +ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLES TO anon; +ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLES TO authenticated; +ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLES TO service_role; +ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT SELECT ON TABLES TO grafana_readonly; +ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT SELECT ON TABLES TO make_readonly; +ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT SELECT ON TABLES TO snaplet_readonly_2; + + +-- +-- Name: DEFAULT PRIVILEGES FOR TABLES; Type: DEFAULT ACL; Schema: public; Owner: supabase_admin +-- + +ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLES TO postgres; +ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLES TO anon; +ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLES TO authenticated; +ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLES TO service_role; + + +-- +-- PostgreSQL database dump complete +-- + diff --git a/docs/plans/002-oss-migration/002b-task5-generation/sections/01-extensions.sql b/docs/plans/002-oss-migration/002b-task5-generation/sections/01-extensions.sql new file mode 100644 index 0000000..1849e80 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/sections/01-extensions.sql @@ -0,0 +1,27 @@ +-- ============================================================================= +-- OSS baseline init migration +-- ============================================================================= +-- Canonical baseline schema for the NXT Grid mini-grid management platform, +-- derived from the legacy migration chain (legacy/supabase/migrations/) minus +-- dead/deprecated/company-specific objects. Every deviation from the legacy +-- chain is recorded in docs/plans/002-oss-migration/002b-schema-deviation-register.md +-- (register entries #1-#34 and the Column/Programmability/Performance/Data API/ +-- FK adjustment sections) — that file is the authoritative source for "why" any +-- given line here differs from the legacy chain. +-- ============================================================================= + +-- ============================================================================= +-- Extensions +-- ============================================================================= +-- Only extensions NOT already enabled on a fresh Supabase Postgres image are +-- created here. Platform defaults (pg_stat_statements, pgcrypto, +-- supabase_vault, uuid-ossp), pg_graphql, and advisor tooling (hypopg, +-- index_advisor) are intentionally omitted — see register #11. +-- pgjwt is intentionally NOT enabled: unavailable on Postgres 17 and confirmed +-- unused in the legacy schema/app code (register #11, amended 2026-07-10). + +CREATE EXTENSION IF NOT EXISTS "postgis" WITH SCHEMA "extensions"; + +CREATE EXTENSION IF NOT EXISTS "pg_net" WITH SCHEMA "extensions"; + +CREATE EXTENSION IF NOT EXISTS "pgsodium"; diff --git a/docs/plans/002-oss-migration/002b-task5-generation/sections/02-types.sql b/docs/plans/002-oss-migration/002b-task5-generation/sections/02-types.sql new file mode 100644 index 0000000..ef12ee7 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/sections/02-types.sql @@ -0,0 +1,284 @@ +-- ============================================================================= +-- Enum types +-- ============================================================================= +-- 31 of the legacy chain's 42 enum types are kept. Dropped (register #1, #7, +-- #13, #14): directive_direction_enum, directive_error_enum, +-- directive_phase_enum, directive_special_status_enum, directive_status_enum, +-- directive_type_enum, meter_credit_transfer_status_enum, payout_status_enum, +-- pd_action_status_enum, pd_action_type_enum, pd_document_type_enum. +-- Value trims: external_system_enum drops JOTFORM/STEAMACO/ACREL (register +-- #28); notification_type_enum drops AUTO_PAYOUT_GENRATION_REPORT (register +-- #29). organization_type_enum gains PLATFORM_OPERATOR (register #22 — the +-- DB-native admin-organization flag; see rls_check_if_admin_org_member()). + +CREATE TYPE public.account_type_enum AS ENUM ( + 'AGENT', + 'MEMBER', + 'CUSTOMER' +); + +CREATE TYPE public.communication_protocol_enum AS ENUM ( + 'CALIN_V1', + 'CALIN_V2', + 'CALIN_LORAWAN' +); + +CREATE TYPE public.currency_enum AS ENUM ( + 'USD', + 'NGN', + 'EUR' +); + +-- external_system_enum: JOTFORM, STEAMACO, ACREL dropped (register #28) +CREATE TYPE public.external_system_enum AS ENUM ( + 'CALIN', + 'SOLCAST', + 'VICTRON', + 'FLUTTERWAVE', + 'AFRICASTALKING', + 'EPICOLLECT', + 'JIRA', + 'TELEGRAM', + 'ZEROTIER', + 'MAKE', + 'FLOW_XO', + 'SENDGRID' +); + +CREATE TYPE public.fs_command_type_enum AS ENUM ( + 'ON', + 'OFF' +); + +CREATE TYPE public.gender_enum AS ENUM ( + 'MALE', + 'FEMALE' +); + +CREATE TYPE public.generator_type_enum AS ENUM ( + 'SMALL', + 'LARGE' +); + +CREATE TYPE public.id_document_type_enum AS ENUM ( + 'PASSPORT', + 'NATIONAL_ID', + 'DRIVING_LICENSE', + 'VOTERS_CARD' +); + +CREATE TYPE public.issue_status_enum AS ENUM ( + 'OPEN', + 'CLOSED', + 'OVERRIDDEN' +); + +CREATE TYPE public.issue_type_enum AS ENUM ( + 'NO_COMMUNICATION', + 'METER_NOT_ACTIVATED', + 'TAMPER', + 'POWER_LIMIT_BREACHED', + 'OVER_VOLTAGE', + 'LOW_VOLTAGE', + 'POWER_LIMIT_BAD_CONFIGURATION', + 'METER_STATE_BAD_CONFIGURATION', + 'UNEXPECTED_POWER_LIMIT', + 'UNEXPECTED_METER_STATUS', + 'NO_CREDIT', + 'NO_CONSUMPTION', + 'NUMBER_OF_PHASES', + 'VEBUS_STATE', + 'VEBUS_ERROR', + 'QUATTRO_TEMPERATURE_ALARM', + 'QUATTRO_OVERLOAD_ALARM', + 'HIGH_BATTERY_TEMPERATURE_ALARM', + 'CELL_IMBALANCE_ALARM', + 'HIGH_CHARGE_CURRENT_ALARM', + 'HIGH_CHARGE_TEMPERATURE_ALARM', + 'BATTERY_INTERNAL_FAILURE', + 'BATTERY_CHARGE_BLOCKED_ALARM', + 'BATTERY_DISCHARGE_BLOCKED_ALARM' +); + +CREATE TYPE public.member_type_enum AS ENUM ( + 'SUPERADMIN', + 'ADMIN', + 'PARTNER', + 'FINANCE', + 'DEVELOPER', + 'MANAGER', + 'SUPPORT', + 'SERVICE', + 'SALES', + 'TECH' +); + +CREATE TYPE public.meter_commissioning_status_enum AS ENUM ( + 'PENDING', + 'PROCESSING', + 'SUCCESSFUL', + 'FAILED' +); + +CREATE TYPE public.meter_interaction_status_enum AS ENUM ( + 'QUEUED', + 'ABORTED', + 'PROCESSING', + 'SUCCESSFUL', + 'FAILED', + 'DEFERRED', + 'SUSPENDED' +); + +CREATE TYPE public.meter_interaction_type_enum AS ENUM ( + 'READ_CREDIT', + 'READ_POWER_LIMIT', + 'READ_VOLTAGE', + 'SET_POWER_LIMIT', + 'TOP_UP', + 'TURN_ON', + 'TURN_OFF', + 'READ_POWER', + 'READ_CURRENT', + 'CLEAR_CREDIT', + 'CLEAR_TAMPER', + 'READ_REPORT', + 'JOIN_NETWORK', + 'DELIVER_PREEXISTING_TOKEN', + 'READ_VERSION', + 'READ_DATE', + 'SET_DATE', + 'READ_TIME', + 'SET_TIME' +); + +CREATE TYPE public.meter_phase_enum AS ENUM ( + 'SINGLE_PHASE', + 'THREE_PHASE' +); + +CREATE TYPE public.meter_type_enum AS ENUM ( + 'HPS', + 'FS' +); + +CREATE TYPE public.mhi_operation_enum AS ENUM ( + 'ADD', + 'REMOVE' +); + +CREATE TYPE public.mhi_status_enum AS ENUM ( + 'PENDING', + 'PROCESSING', + 'SUCCESSFUL', + 'FAILED' +); + +CREATE TYPE public.mppt_type_enum AS ENUM ( + 'MPPT', + 'PV_INVERTER' +); + +CREATE TYPE public.notification_status_enum AS ENUM ( + 'PENDING', + 'PROCESSING', + 'RECEIVED_BY_API', + 'FAILED', + 'SUCCESSFUL', + 'READ', + 'UNKNOWN' +); + +-- notification_type_enum: AUTO_PAYOUT_GENRATION_REPORT dropped (register #29) +CREATE TYPE public.notification_type_enum AS ENUM ( + 'GRID_IS_HPS_ON_STATE_CHANGE', + 'GRID_IS_FS_ON_STATE_CHANGE', + 'GRID_METERING_HARDWARE_STATE_CHANGE', + 'FS_RULE_EXECUTION_COMING_UP', + 'FS_RULE_CHANGED', + 'TARIFF_RULE_CHANGED', + 'CLEAN_PANELS_REMINDER', + 'GRID_REVENUE', + 'PASSWORD_RESET', + 'INVITE', + 'CREDIT_SENT', + 'CREDIT_RECEIVED', + 'METER_TOPPED_UP', + 'PAYMENT_REJECTED', + 'SITE_SUBMISSION' +); + +CREATE TYPE public.order_actor_type_enum AS ENUM ( + 'BANKING_SYSTEM', + 'ORGANIZATION', + 'CONNECTION', + 'METER', + 'AGENT', + 'CUSTOMER' +); + +CREATE TYPE public.order_status_enum AS ENUM ( + 'INITIALISED', + 'PENDING', + 'COMPLETED', + 'FAILED', + 'CANCELLED', + 'TIMED_OUT', + 'IGNORED' +); + +CREATE TYPE public.order_type_enum AS ENUM ( + 'ENERGY_TOPUP', + 'CONNECTION_PAYMENT', + 'CONNECTION_REFUND', + 'AGENT_WITHDRAWAL', + 'AGENT_TOPUP', + 'ORGANIZATION_TOPUP', + 'ORGANIZATION_WITHDRAWAL', + 'CUSTOMER_TOPUP' +); + +-- organization_type_enum: PLATFORM_OPERATOR added (register #22) +CREATE TYPE public.organization_type_enum AS ENUM ( + 'SOLAR_DEVELOPER', + 'LENDER', + 'DATA_AGGREGATOR', + 'PLATFORM_OPERATOR' +); + +CREATE TYPE public.payment_channel_enum AS ENUM ( + 'USSD', + 'AYRTON', + 'NIFFLER', + 'TELEGRAM' +); + +CREATE TYPE public.payment_method_enum AS ENUM ( + 'CREDIT_CARD', + 'USSD', + 'BANK_TRANSFER' +); + +CREATE TYPE public.solcast_cache_request_type_enum AS ENUM ( + 'ESTIMATED_ACTUALS', + 'FORECAST' +); + +CREATE TYPE public.transaction_status_enum AS ENUM ( + 'SUCCESSFUL', + 'FAILED' +); + +CREATE TYPE public.wallet_type_enum AS ENUM ( + 'VIRTUAL', + 'REAL' +); + +CREATE TYPE public.weather_type_enum AS ENUM ( + 'CLOUDY', + 'CLOUDS', + 'SHOWERS', + 'SUNNY', + 'UNKNOWN', + 'CLOUDY_WITH_RAIN' +); diff --git a/docs/plans/002-oss-migration/002b-task5-generation/sections/04-tables-body.sql b/docs/plans/002-oss-migration/002b-task5-generation/sections/04-tables-body.sql new file mode 100644 index 0000000..647907e --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/sections/04-tables-body.sql @@ -0,0 +1,564 @@ +CREATE TABLE public.accounts ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + full_name character varying, + email character varying, + phone character varying, + telegram_id character varying, + telegram_link_token character varying, + deleted_at timestamp(3) without time zone, + supabase_id uuid, + organization_id integer +); + +CREATE TABLE public.agents ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + grid_id integer, + account_id integer, + rls_organization_id integer +); + +CREATE TABLE public.api_keys ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + key character varying, + account_id integer +); + +CREATE TABLE public.audits ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + message character varying NOT NULL, + from_fs_command boolean DEFAULT false NOT NULL, + author_id integer, + grid_id integer, + organization_id integer, + meter_id integer, + agent_id integer, + customer_id integer, + member_id integer, + connection_id integer, + dcu_id integer +); + +CREATE TABLE public.banks ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + name character varying NOT NULL, + external_id character varying NOT NULL +); + +CREATE TABLE public.dcus ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + external_reference character varying NOT NULL, + external_system public.external_system_enum NOT NULL, + is_online boolean DEFAULT false NOT NULL, + last_online_at timestamp(3) with time zone, + is_online_updated_at timestamp(3) with time zone, + communication_protocol public.communication_protocol_enum, + grid_id integer, + last_metering_hardware_install_session_id integer, + rls_organization_id integer, + location_geom extensions.geometry(Point,4326) +); + +CREATE TABLE public.meters ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + external_reference character varying NOT NULL, + deleted_at timestamp(3) without time zone, + balance double precision, + balance_updated_at timestamp(3) with time zone, + kwh_credit_available double precision, + kwh_credit_available_updated_at timestamp(3) with time zone, + last_non_zero_consumption_at timestamp(3) with time zone, + is_on boolean DEFAULT false, + should_be_on boolean DEFAULT false, + is_on_updated_at timestamp(3) with time zone, + should_be_on_updated_at timestamp(3) with time zone, + is_manual_mode_on boolean DEFAULT false NOT NULL, + is_manual_mode_on_updated_at timestamp(3) with time zone, + voltage double precision, + voltage_updated_at timestamp(3) with time zone, + power double precision, + power_updated_at timestamp(3) with time zone, + latitude double precision, + longitude double precision, + coord_accuracy double precision DEFAULT '0'::double precision NOT NULL, + power_limit integer, + power_limit_updated_at timestamp(3) with time zone, + power_limit_should_be integer, + power_limit_should_be_updated_at timestamp(3) with time zone, + is_starred boolean DEFAULT false NOT NULL, + external_system public.external_system_enum NOT NULL, + meter_type public.meter_type_enum DEFAULT 'HPS'::public.meter_type_enum NOT NULL, + nickname character varying, + last_seen_at timestamp(3) with time zone, + issue_check_execution_session character varying, + issue_check_last_run_at timestamp(3) with time zone, + pole_id integer, + meter_phase public.meter_phase_enum DEFAULT 'SINGLE_PHASE'::public.meter_phase_enum NOT NULL, + last_metering_hardware_install_session_id integer, + kwh_tariff double precision, + version character varying, + power_limit_hps_mode integer DEFAULT 200 NOT NULL, + communication_protocol public.communication_protocol_enum DEFAULT 'CALIN_LORAWAN'::public.communication_protocol_enum, + is_cabin_meter boolean DEFAULT false NOT NULL, + last_encountered_issue_id integer, + connection_id integer, + dcu_id integer, + decoder_key text, + last_sts_token_issued_at timestamp with time zone, + rls_grid_id integer, + rls_organization_id integer, + is_test_mode_on boolean DEFAULT false NOT NULL, + connection_metrics jsonb +); + +CREATE TABLE public.poles ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + external_reference character varying(10) NOT NULL, + nickname character varying, + is_virtual boolean DEFAULT false NOT NULL, + location_accuracy double precision, + grid_id integer, + location_geom extensions.geometry(Point,4326) NOT NULL, + rls_organization_id integer +); + +CREATE TABLE public.connection_requested_meters ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + meter_type public.meter_type_enum NOT NULL, + meter_phase public.meter_phase_enum NOT NULL, + fee double precision DEFAULT '0'::double precision NOT NULL, + deleted_at timestamp(3) without time zone, + connection_id integer NOT NULL, + rls_organization_id integer +); + +CREATE TABLE public.connections ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + deleted_at timestamp(3) without time zone, + document_type public.id_document_type_enum DEFAULT 'PASSPORT'::public.id_document_type_enum, + document_id character varying, + upload_uuid character varying, + external_system public.external_system_enum DEFAULT 'EPICOLLECT'::public.external_system_enum, + paid double precision DEFAULT '0'::double precision NOT NULL, + currency public.currency_enum NOT NULL, + women_impacted integer NOT NULL, + is_lifeline boolean, + is_public boolean NOT NULL, + is_commercial boolean NOT NULL, + is_residential boolean NOT NULL, + is_building_wired boolean DEFAULT false, + is_using_led_bulbs boolean DEFAULT false, + customer_id integer, + rls_organization_id integer +); + +CREATE TABLE public.customers ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + gender public.gender_enum, + total_connection_fee double precision DEFAULT '0'::double precision NOT NULL, + total_connection_paid double precision DEFAULT '0'::double precision NOT NULL, + is_hidden_from_reporting boolean DEFAULT false NOT NULL, + lives_primarily_in_the_community boolean DEFAULT true NOT NULL, + latitude double precision, + longitude double precision, + generator_owned public.generator_type_enum, + grid_id integer, + account_id integer, + rls_organization_id integer +); + +CREATE TABLE public.meter_command_batch_executions ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + pending_count integer DEFAULT 0 NOT NULL, + processing_count integer DEFAULT 0 NOT NULL, + successful_count integer DEFAULT 0 NOT NULL, + failed_count integer DEFAULT 0 NOT NULL, + processed_count integer DEFAULT 0 NOT NULL, + total_count integer DEFAULT 0 NOT NULL, + meter_command_batch_id integer NOT NULL, + rls_organization_id integer, + completed_at timestamp with time zone, + qualified_at timestamp with time zone +); + +CREATE TABLE public.meter_command_batches ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + is_deleted boolean DEFAULT false NOT NULL, + hour integer NOT NULL, + minute integer NOT NULL, + is_repeating boolean DEFAULT false NOT NULL, + grid_id integer, + fs_command public.fs_command_type_enum, + author_id integer, + updated_at timestamp(3) with time zone, + rls_organization_id integer, + task_type public.meter_interaction_type_enum +); + +CREATE TABLE public.energy_cabins ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + grid_id integer NOT NULL, + location_geom extensions.geometry(Point,4326) NOT NULL, + rls_organization_id integer +); + +CREATE TABLE public.grids ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + deployed_at timestamp(3) with time zone, + commissioned_at timestamp(3) with time zone, + name character varying NOT NULL, + is_hps_on boolean DEFAULT false NOT NULL, + is_hps_on_updated_at timestamp(3) with time zone, + walkthrough_external_id character varying, + timezone character varying DEFAULT 'Africa/Lagos'::character varying NOT NULL, + kwp double precision DEFAULT '0'::double precision NOT NULL, + kwh double precision DEFAULT '0'::double precision NOT NULL, + kwp_tariff double precision DEFAULT '0'::double precision NOT NULL, + kwh_tariff double precision DEFAULT '0'::double precision NOT NULL, + kwh_tariff_essential_service double precision DEFAULT '0'::double precision NOT NULL, + kwh_tariff_full_service double precision DEFAULT '0'::double precision NOT NULL, + current_weather public.weather_type_enum, + generation_external_system public.external_system_enum DEFAULT 'VICTRON'::public.external_system_enum NOT NULL, + metering_external_system public.external_system_enum DEFAULT 'CALIN'::public.external_system_enum NOT NULL, + generation_external_site_id character varying, + generation_external_gateway_id character varying, + generation_gateway_last_seen_at timestamp(3) with time zone, + is_fs_on boolean DEFAULT false NOT NULL, + is_fs_on_updated_at timestamp(3) with time zone, + should_fs_be_on boolean DEFAULT false NOT NULL, + should_fs_be_on_updated_at timestamp(3) with time zone, + default_hps_connection_fee double precision DEFAULT '0'::double precision NOT NULL, + default_fs_1_phase_connection_fee double precision DEFAULT '0'::double precision NOT NULL, + default_fs_3_phase_connection_fee double precision DEFAULT '0'::double precision NOT NULL, + monthly_rental double precision DEFAULT '0'::double precision NOT NULL, + is_hidden_from_reporting boolean DEFAULT true NOT NULL, + is_three_phase_supported boolean DEFAULT false NOT NULL, + is_using_vsat boolean DEFAULT false NOT NULL, + is_using_mobile_network boolean DEFAULT false NOT NULL, + is_hps_on_threshold_kw double precision DEFAULT 0 NOT NULL, + kwh_per_battery_module double precision, + identifier integer, + lifeline_connection_kwh_threshold integer DEFAULT 5 NOT NULL, + lifeline_connection_days_threshold integer DEFAULT 30 NOT NULL, + is_panel_cleaning_notification_enabled boolean DEFAULT false NOT NULL, + is_energised_notification_enabled boolean DEFAULT false NOT NULL, + is_fs_on_notification_enabled boolean DEFAULT false NOT NULL, + is_metering_hardware_online_notification_enabled boolean DEFAULT false NOT NULL, + is_tariff_change_notification_enabled boolean DEFAULT false NOT NULL, + is_upcoming_fs_control_rule_notification_enabled boolean DEFAULT false NOT NULL, + is_fs_control_rule_change_notification_enabled boolean DEFAULT false NOT NULL, + is_automatic_meter_install_enabled boolean DEFAULT false NOT NULL, + is_automatic_energy_generation_data_sync_enabled boolean DEFAULT false NOT NULL, + is_automatic_meter_energy_consumption_data_sync_enabled boolean DEFAULT false NOT NULL, + is_dcu_connectivity_tracking_enabled boolean DEFAULT false NOT NULL, + is_router_connectivity_tracking_enabled boolean DEFAULT false NOT NULL, + is_cabin_meter_credit_depleting boolean DEFAULT false NOT NULL, + telegram_response_path_token character varying, + telegram_notification_channel_invite_link character varying, + internal_telegram_group_chat_id character varying, + internal_telegram_group_thread_id character varying, + organization_id integer NOT NULL, + meter_commissioning_initial_credit_kwh real DEFAULT '0'::real NOT NULL, + is_generation_managed_by_nxt_grid boolean DEFAULT true NOT NULL, + location_geom extensions.geometry(Point,4326), + telegram_config jsonb DEFAULT '{}'::jsonb, + feature_access_config jsonb DEFAULT '{}'::jsonb NOT NULL +); + +CREATE TABLE public.issues ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + issue_type public.issue_type_enum NOT NULL, + issue_status public.issue_status_enum DEFAULT 'OPEN'::public.issue_status_enum NOT NULL, + external_tracking_system public.external_system_enum DEFAULT 'JIRA'::public.external_system_enum NOT NULL, + started_at timestamp(3) with time zone, + closed_at timestamp(3) with time zone, + external_tracking_reference character varying, + meter_id integer, + rls_organization_id integer +); + +CREATE TABLE public.members ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + subscribed_to_telegram_revenue_notifications boolean DEFAULT false NOT NULL, + member_type public.member_type_enum DEFAULT 'DEVELOPER'::public.member_type_enum NOT NULL, + account_id integer, + busy_commissioning_id integer, + training_level smallint DEFAULT '0'::smallint NOT NULL, + rls_organization_id integer, + hidden boolean DEFAULT false NOT NULL +); + +CREATE TABLE public.meter_commissionings ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + meter_commissioning_status public.meter_commissioning_status_enum DEFAULT 'PROCESSING'::public.meter_commissioning_status_enum NOT NULL, + metering_hardware_install_session_id integer, + rls_organization_id integer +); + +CREATE TABLE public.meter_interactions ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + meter_id integer NOT NULL, + token text, + order_id integer, + transactive_kwh real, + target_power_limit integer, + result_value jsonb, + meter_interaction_type public.meter_interaction_type_enum NOT NULL, + meter_interaction_status public.meter_interaction_status_enum DEFAULT 'QUEUED'::public.meter_interaction_status_enum NOT NULL, + batch_execution_id integer, + meter_commissioning_id integer, + delivery_failure_history jsonb, + payload_data jsonb +); + +CREATE TABLE public.metering_hardware_imports ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + metering_hardware_import_operation public.mhi_operation_enum DEFAULT 'ADD'::public.mhi_operation_enum NOT NULL, + metering_hardware_import_status public.mhi_status_enum DEFAULT 'PENDING'::public.mhi_status_enum NOT NULL, + metering_hardware_install_session_id integer NOT NULL, + lock_session character varying, + rls_organization_id integer +); + +CREATE TABLE public.metering_hardware_install_sessions ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + dcu_id integer, + meter_id integer, + author_id integer, + last_meter_commissioning_id integer, + last_metering_hardware_import_id integer, + rls_organization_id integer +); + +CREATE TABLE public.mppts ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + external_reference character varying NOT NULL, + external_id character varying NOT NULL, + external_system public.external_system_enum DEFAULT 'VICTRON'::public.external_system_enum NOT NULL, + kw double precision, + azimuth double precision, + tilt double precision, + installed_at timestamp(3) with time zone DEFAULT now() NOT NULL, + deleted_at timestamp(3) with time zone, + mppt_type public.mppt_type_enum DEFAULT 'MPPT'::public.mppt_type_enum NOT NULL, + grid_id integer, + rls_organization_id integer +); + +CREATE TABLE public.notes ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + message character varying, + customer_id integer, + connection_id integer, + meter_id integer, + author_id integer, + rls_organization_id integer +); + +CREATE TABLE public.notification_parameters ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + parameters json +); + +CREATE TABLE public.notifications ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + connector_external_system public.external_system_enum, + carrier_external_system public.external_system_enum, + notification_type public.notification_type_enum NOT NULL, + notification_status public.notification_status_enum NOT NULL, + external_reference character varying, + notification_parameter_id integer, + grid_id integer, + organization_id integer, + account_id integer, + lock_session character varying, + message character varying, + phone character varying, + subject character varying, + email character varying, + chat_id character varying, + thread_id character varying +); + +CREATE TABLE public.orders ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + amount double precision NOT NULL, + order_status public.order_status_enum DEFAULT 'PENDING'::public.order_status_enum NOT NULL, + lock_session character varying, + external_reference character varying, + currency public.currency_enum NOT NULL, + tariff_type public.meter_type_enum, + tariff double precision DEFAULT '-1'::double precision NOT NULL, + payment_method public.payment_method_enum, + payment_channel public.payment_channel_enum, + author_id integer, + historical_grid_id integer, + meta_author_type public.account_type_enum, + meta_author_name character varying, + meta_author_id integer, + meta_order_type public.order_type_enum, + meta_sender_id integer, + meta_sender_name character varying, + meta_receiver_name character varying, + meta_receiver_name_part_2 character varying, + meta_sender_name_part_2 character varying, + meta_receiver_id integer, + meta_sender_type public.order_actor_type_enum, + meta_receiver_type public.order_actor_type_enum, + meta_is_hidden_from_reporting boolean, + sender_wallet_id integer, + receiver_wallet_id integer, + ussd_session_id integer, + meta_receiver_id_part_2 integer, + rls_organization_id integer, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + +CREATE TABLE public.organizations ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + name character varying NOT NULL, + formal_name character varying, + email character varying, + epicollect_contract_survey_slug character varying, + epicollect_contract_survey_secret character varying, + epicollect_contract_survey_client_id character varying, + epicollect_contract_last_sync_at timestamp(3) with time zone, + developer_group_telegram_chat_id character varying, + deleted_at timestamp with time zone, + organization_type public.organization_type_enum DEFAULT 'SOLAR_DEVELOPER'::public.organization_type_enum NOT NULL +); + +CREATE TABLE public.pd_site_submissions ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + author_full_name text NOT NULL, + author_email text NOT NULL, + author_organization_name text NOT NULL, + site_name character varying NOT NULL, + site_details jsonb NOT NULL, + outline_geom extensions.geometry(Polygon,4326), + deleted_at timestamp with time zone, + location_geom extensions.geometry(Point,4326), + organization_id integer, + author_organization_id integer, + buildings_geo_flat jsonb, + distribution_geo_flat jsonb, + meta_geo_flat jsonb, + poles_geo_flat jsonb +); + +CREATE TABLE public.pd_sites ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + organization_id integer, + name text NOT NULL, + deleted_at timestamp with time zone, + location_geom extensions.geometry(Point,4326), + outline_geom extensions.geometry(Polygon,4326), + operations_grid_id integer +); + +CREATE TABLE public.routers ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + external_reference character varying NOT NULL, + external_system public.external_system_enum DEFAULT 'ZEROTIER'::public.external_system_enum NOT NULL, + is_online boolean DEFAULT false NOT NULL, + is_online_updated_at timestamp(3) with time zone, + deleted_at timestamp(3) with time zone, + grid_id integer, + rls_organization_id integer +); + +CREATE TABLE public.solcast_cache ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + request_type public.solcast_cache_request_type_enum NOT NULL, + latitude numeric(8,6) NOT NULL, + longitude numeric(9,6) NOT NULL, + tilt numeric(10,3) NOT NULL, + azimuth numeric(10,3) NOT NULL, + capacity_kwp numeric(10,3) NOT NULL, + install_date character varying NOT NULL, + response text NOT NULL +); + +CREATE TABLE public.transactions ( + id bigint NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + amount double precision NOT NULL, + transaction_status public.transaction_status_enum NOT NULL, + balance_before double precision DEFAULT '-1'::double precision NOT NULL, + balance_after double precision DEFAULT '-1'::double precision NOT NULL, + wallet_id integer, + order_id integer, + rls_organization_id integer +); + +CREATE TABLE public.ussd_session_hops ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + text character varying, + phone character varying, + network_code character varying, + service_code character varying, + ussd_session_id integer +); + +CREATE TABLE public.ussd_sessions ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + phone character varying NOT NULL, + amount double precision, + external_reference character varying, + external_system public.external_system_enum NOT NULL, + is_using_other_option boolean DEFAULT false NOT NULL, + account_id integer, + meter_id integer, + bank_id integer +); + +CREATE TABLE public.wallets ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + organization_id integer, + agent_id integer, + customer_id integer, + meter_id integer, + lock_session character varying, + balance double precision DEFAULT '0'::double precision NOT NULL, + balance_updated_at timestamp(3) with time zone, + identifier character varying, + wallet_type public.wallet_type_enum DEFAULT 'REAL'::public.wallet_type_enum NOT NULL, + connection_id integer, + rls_organization_id integer +); + diff --git a/docs/plans/002-oss-migration/002b-task5-generation/sections/05-sequences-body.sql b/docs/plans/002-oss-migration/002b-task5-generation/sections/05-sequences-body.sql new file mode 100644 index 0000000..aad0f44 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/sections/05-sequences-body.sql @@ -0,0 +1,409 @@ +CREATE SEQUENCE public.accounts_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.accounts ALTER COLUMN id SET DEFAULT nextval('public.accounts_id_seq'::regclass); + +ALTER SEQUENCE public.accounts_id_seq OWNED BY public.accounts.id; + +CREATE SEQUENCE public.agents_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.agents ALTER COLUMN id SET DEFAULT nextval('public.agents_id_seq'::regclass); + +ALTER SEQUENCE public.agents_id_seq OWNED BY public.agents.id; + +CREATE SEQUENCE public.api_keys_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.api_keys ALTER COLUMN id SET DEFAULT nextval('public.api_keys_id_seq'::regclass); + +ALTER SEQUENCE public.api_keys_id_seq OWNED BY public.api_keys.id; + +CREATE SEQUENCE public.audits_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.audits ALTER COLUMN id SET DEFAULT nextval('public.audits_id_seq'::regclass); + +ALTER SEQUENCE public.audits_id_seq OWNED BY public.audits.id; + +CREATE SEQUENCE public.banks_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.banks ALTER COLUMN id SET DEFAULT nextval('public.banks_id_seq'::regclass); + +ALTER SEQUENCE public.banks_id_seq OWNED BY public.banks.id; + +CREATE SEQUENCE public.dcus_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.dcus ALTER COLUMN id SET DEFAULT nextval('public.dcus_id_seq'::regclass); + +ALTER SEQUENCE public.dcus_id_seq OWNED BY public.dcus.id; + +CREATE SEQUENCE public.meters_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.meters ALTER COLUMN id SET DEFAULT nextval('public.meters_id_seq'::regclass); + +ALTER SEQUENCE public.meters_id_seq OWNED BY public.meters.id; + +CREATE SEQUENCE public.poles_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.poles ALTER COLUMN id SET DEFAULT nextval('public.poles_id_seq'::regclass); + +ALTER SEQUENCE public.poles_id_seq OWNED BY public.poles.id; + +CREATE SEQUENCE public.connection_requested_meters_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.connection_requested_meters ALTER COLUMN id SET DEFAULT nextval('public.connection_requested_meters_id_seq'::regclass); + +ALTER SEQUENCE public.connection_requested_meters_id_seq OWNED BY public.connection_requested_meters.id; + +CREATE SEQUENCE public.connections_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.connections ALTER COLUMN id SET DEFAULT nextval('public.connections_id_seq'::regclass); + +ALTER SEQUENCE public.connections_id_seq OWNED BY public.connections.id; + +CREATE SEQUENCE public.customers_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.customers ALTER COLUMN id SET DEFAULT nextval('public.customers_id_seq'::regclass); + +ALTER SEQUENCE public.customers_id_seq OWNED BY public.customers.id; + +CREATE SEQUENCE public.meter_command_batch_executions_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.meter_command_batch_executions ALTER COLUMN id SET DEFAULT nextval('public.meter_command_batch_executions_id_seq'::regclass); + +ALTER SEQUENCE public.meter_command_batch_executions_id_seq OWNED BY public.meter_command_batch_executions.id; + +CREATE SEQUENCE public.meter_command_batches_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.meter_command_batches ALTER COLUMN id SET DEFAULT nextval('public.meter_command_batches_id_seq'::regclass); + +ALTER SEQUENCE public.meter_command_batches_id_seq OWNED BY public.meter_command_batches.id; + +CREATE SEQUENCE public.energy_cabins_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.energy_cabins ALTER COLUMN id SET DEFAULT nextval('public.energy_cabins_id_seq'::regclass); + +ALTER SEQUENCE public.energy_cabins_id_seq OWNED BY public.energy_cabins.id; + +CREATE SEQUENCE public.grids_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.grids ALTER COLUMN id SET DEFAULT nextval('public.grids_id_seq'::regclass); + +ALTER SEQUENCE public.grids_id_seq OWNED BY public.grids.id; + +CREATE SEQUENCE public.issues_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.issues ALTER COLUMN id SET DEFAULT nextval('public.issues_id_seq'::regclass); + +ALTER SEQUENCE public.issues_id_seq OWNED BY public.issues.id; + +CREATE SEQUENCE public.members_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.members ALTER COLUMN id SET DEFAULT nextval('public.members_id_seq'::regclass); + +ALTER SEQUENCE public.members_id_seq OWNED BY public.members.id; + +CREATE SEQUENCE public.meter_commissionings_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.meter_commissionings ALTER COLUMN id SET DEFAULT nextval('public.meter_commissionings_id_seq'::regclass); + +ALTER SEQUENCE public.meter_commissionings_id_seq OWNED BY public.meter_commissionings.id; + +ALTER TABLE public.meter_interactions ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.meter_interactions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + +CREATE SEQUENCE public.metering_hardware_imports_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.metering_hardware_imports ALTER COLUMN id SET DEFAULT nextval('public.metering_hardware_imports_id_seq'::regclass); + +ALTER SEQUENCE public.metering_hardware_imports_id_seq OWNED BY public.metering_hardware_imports.id; + +CREATE SEQUENCE public.metering_hardware_install_sessions_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.metering_hardware_install_sessions ALTER COLUMN id SET DEFAULT nextval('public.metering_hardware_install_sessions_id_seq'::regclass); + +ALTER SEQUENCE public.metering_hardware_install_sessions_id_seq OWNED BY public.metering_hardware_install_sessions.id; + +CREATE SEQUENCE public.mppts_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.mppts ALTER COLUMN id SET DEFAULT nextval('public.mppts_id_seq'::regclass); + +ALTER SEQUENCE public.mppts_id_seq OWNED BY public.mppts.id; + +CREATE SEQUENCE public.notes_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.notes ALTER COLUMN id SET DEFAULT nextval('public.notes_id_seq'::regclass); + +ALTER SEQUENCE public.notes_id_seq OWNED BY public.notes.id; + +CREATE SEQUENCE public.notification_parameters_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.notification_parameters ALTER COLUMN id SET DEFAULT nextval('public.notification_parameters_id_seq'::regclass); + +ALTER SEQUENCE public.notification_parameters_id_seq OWNED BY public.notification_parameters.id; + +CREATE SEQUENCE public.notifications_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.notifications ALTER COLUMN id SET DEFAULT nextval('public.notifications_id_seq'::regclass); + +ALTER SEQUENCE public.notifications_id_seq OWNED BY public.notifications.id; + +CREATE SEQUENCE public.orders_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.orders ALTER COLUMN id SET DEFAULT nextval('public.orders_id_seq'::regclass); + +ALTER SEQUENCE public.orders_id_seq OWNED BY public.orders.id; + +CREATE SEQUENCE public.organizations_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.organizations ALTER COLUMN id SET DEFAULT nextval('public.organizations_id_seq'::regclass); + +ALTER SEQUENCE public.organizations_id_seq OWNED BY public.organizations.id; + +ALTER TABLE public.pd_site_submissions ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.pd_site_submissions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + +ALTER TABLE public.pd_sites ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.pd_sites_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + +CREATE SEQUENCE public.routers_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.routers ALTER COLUMN id SET DEFAULT nextval('public.routers_id_seq'::regclass); + +ALTER SEQUENCE public.routers_id_seq OWNED BY public.routers.id; + +CREATE SEQUENCE public.solcast_cache_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.solcast_cache ALTER COLUMN id SET DEFAULT nextval('public.solcast_cache_id_seq'::regclass); + +ALTER SEQUENCE public.solcast_cache_id_seq OWNED BY public.solcast_cache.id; + +CREATE SEQUENCE public.transactions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.transactions ALTER COLUMN id SET DEFAULT nextval('public.transactions_id_seq'::regclass); + +ALTER SEQUENCE public.transactions_id_seq OWNED BY public.transactions.id; + +CREATE SEQUENCE public.ussd_session_hops_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.ussd_session_hops ALTER COLUMN id SET DEFAULT nextval('public.ussd_session_hops_id_seq'::regclass); + +ALTER SEQUENCE public.ussd_session_hops_id_seq OWNED BY public.ussd_session_hops.id; + +CREATE SEQUENCE public.ussd_sessions_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.ussd_sessions ALTER COLUMN id SET DEFAULT nextval('public.ussd_sessions_id_seq'::regclass); + +ALTER SEQUENCE public.ussd_sessions_id_seq OWNED BY public.ussd_sessions.id; + +CREATE SEQUENCE public.wallets_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.wallets ALTER COLUMN id SET DEFAULT nextval('public.wallets_id_seq'::regclass); + +ALTER SEQUENCE public.wallets_id_seq OWNED BY public.wallets.id; diff --git a/docs/plans/002-oss-migration/002b-task5-generation/sections/06-constraints-body.sql b/docs/plans/002-oss-migration/002b-task5-generation/sections/06-constraints-body.sql new file mode 100644 index 0000000..c2f01de --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/sections/06-constraints-body.sql @@ -0,0 +1,167 @@ +ALTER TABLE ONLY public.connections + ADD CONSTRAINT "PK_0a1f844af3122354cbd487a8d03" PRIMARY KEY (id); + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "PK_0a71b52dbb545fa36efaf070583" PRIMARY KEY (id); + +ALTER TABLE ONLY public.customers + ADD CONSTRAINT "PK_133ec679a801fab5e070f73d3ea" PRIMARY KEY (id); + +ALTER TABLE ONLY public.poles + ADD CONSTRAINT "PK_1f4336016e8de1d62acb05ce829" PRIMARY KEY (id); + +ALTER TABLE ONLY public.members + ADD CONSTRAINT "PK_28b53062261b996d9c99fa12404" PRIMARY KEY (id); + +ALTER TABLE ONLY public.notification_parameters + ADD CONSTRAINT "PK_338f36e72b69406bed36e054e09" PRIMARY KEY (id); + +ALTER TABLE ONLY public.banks + ADD CONSTRAINT "PK_3975b5f684ec241e3901db62d77" PRIMARY KEY (id); + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "PK_4eb998262e5c773a89dfe3ea0e1" PRIMARY KEY (id); + +ALTER TABLE ONLY public.ussd_session_hops + ADD CONSTRAINT "PK_56d984bae21ce6e86848a8a3c04" PRIMARY KEY (id); + +ALTER TABLE ONLY public.energy_cabins + ADD CONSTRAINT "PK_59ecb7965974e45fdc2f243bda7" PRIMARY KEY (id); + +ALTER TABLE ONLY public.accounts + ADD CONSTRAINT "PK_5a7a02c20412299d198e097a8fe" PRIMARY KEY (id); + +ALTER TABLE ONLY public.api_keys + ADD CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad" PRIMARY KEY (id); + +ALTER TABLE ONLY public.metering_hardware_imports + ADD CONSTRAINT "PK_665b32b395a7675648ed77da4f6" PRIMARY KEY (id); + +ALTER TABLE ONLY public.meter_commissionings + ADD CONSTRAINT "PK_69fce91302a5dc8dd3db274717b" PRIMARY KEY (id); + +ALTER TABLE ONLY public.notifications + ADD CONSTRAINT "PK_6a72c3c0f683f6462415e653c3a" PRIMARY KEY (id); + +ALTER TABLE ONLY public.organizations + ADD CONSTRAINT "PK_6b031fcd0863e3f6b44230163f9" PRIMARY KEY (id); + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "PK_710e2d4957aa5878dfe94e4ac2f" PRIMARY KEY (id); + +ALTER TABLE ONLY public.dcus + ADD CONSTRAINT "PK_82192607fa5119101a78fe53b7d" PRIMARY KEY (id); + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "PK_8402e5df5a30a229380e83e4f7e" PRIMARY KEY (id); + +ALTER TABLE ONLY public.grids + ADD CONSTRAINT "PK_840d40fdcde1e935afd43082aca" PRIMARY KEY (id); + +ALTER TABLE ONLY public.meter_command_batches + ADD CONSTRAINT "PK_92d424811a3fd49a8020eadb97a" PRIMARY KEY (id); + +ALTER TABLE ONLY public.meter_command_batch_executions + ADD CONSTRAINT "PK_9494ab23abdfe4e59f3f29842d5" PRIMARY KEY (id); + +ALTER TABLE ONLY public.agents + ADD CONSTRAINT "PK_9c653f28ae19c5884d5baf6a1d9" PRIMARY KEY (id); + +ALTER TABLE ONLY public.issues + ADD CONSTRAINT "PK_9d8ecbbeff46229c700f0449257" PRIMARY KEY (id); + +ALTER TABLE ONLY public.solcast_cache + ADD CONSTRAINT "PK_a0b1d6bdbc5ca0056201a1c6dd0" PRIMARY KEY (id); + +ALTER TABLE ONLY public.transactions + ADD CONSTRAINT "PK_a219afd8dd77ed80f5a862f1db9" PRIMARY KEY (id); + +ALTER TABLE ONLY public.notes + ADD CONSTRAINT "PK_af6206538ea96c4e77e9f400c3d" PRIMARY KEY (id); + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "PK_b2d7a2089999197dc7024820f28" PRIMARY KEY (id); + +ALTER TABLE ONLY public.routers + ADD CONSTRAINT "PK_b6d283f1e40d4942dedbc0cb27a" PRIMARY KEY (id); + +ALTER TABLE ONLY public.ussd_sessions + ADD CONSTRAINT "PK_c18f16f36e79b2fb87783476830" PRIMARY KEY (id); + +ALTER TABLE ONLY public.mppts + ADD CONSTRAINT "PK_c8a4dc57b932c173f6c579804c0" PRIMARY KEY (id); + +ALTER TABLE ONLY public.connection_requested_meters + ADD CONSTRAINT "PK_ef90f8d1aa3055757e0ff8e4aa7" PRIMARY KEY (id); + +ALTER TABLE ONLY public.accounts + ADD CONSTRAINT "REL_13bf998d703e7d4c7b9a90f4f7" UNIQUE (supabase_id); + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "REL_4eb64f5decb7250cfa993410c1" UNIQUE (last_meter_commissioning_id); + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "REL_5606d6ec5ab568377509edc526" UNIQUE (last_encountered_issue_id); + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "REL_6580899a2293de27787376887f" UNIQUE (customer_id); + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "REL_86d4557a79e374b5c55cb3b66d" UNIQUE (last_metering_hardware_install_session_id); + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "REL_8db1f4e4f8122bd25d50ad96b2" UNIQUE (meter_id); + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "REL_92081072063eaf51300ab6c267" UNIQUE (ussd_session_id); + +ALTER TABLE ONLY public.dcus + ADD CONSTRAINT "REL_9334b587e6e5291f8ccd1b11c4" UNIQUE (last_metering_hardware_install_session_id); + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "REL_be64a09dc738420a409cb96026" UNIQUE (last_metering_hardware_import_id); + +ALTER TABLE ONLY public.agents + ADD CONSTRAINT "REL_df520decc9e003a843d8edd986" UNIQUE (account_id); + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "REL_e63e504d8e35ef37a2c56b75eb" UNIQUE (connection_id); + +ALTER TABLE ONLY public.customers + ADD CONSTRAINT "REL_ebcc29963874e55053e8ee80be" UNIQUE (account_id); + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "REL_f499c61c6d6a0ac3f794d966ed" UNIQUE (organization_id); + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "REL_f5782e05e8688f0cfbb5c4a52c" UNIQUE (agent_id); + +ALTER TABLE ONLY public.members + ADD CONSTRAINT "REL_fd9dfb97e21b75fc45d42aa614" UNIQUE (account_id); + +ALTER TABLE ONLY public.accounts + ADD CONSTRAINT "UQ_0ec8c228e89a95aeb4af9ee3226" UNIQUE (telegram_link_token); + +ALTER TABLE ONLY public.accounts + ADD CONSTRAINT "UQ_3ffa9ce30e56b6d2abf0a465f5f" UNIQUE (telegram_id); + +ALTER TABLE ONLY public.grids + ADD CONSTRAINT "UQ_61f08f04c9f0f1d3afd24b9be0b" UNIQUE (identifier); + +ALTER TABLE ONLY public.poles + ADD CONSTRAINT "UQ_67112e4334d7090a571d2fff42a" UNIQUE (external_reference); + +ALTER TABLE ONLY public.api_keys + ADD CONSTRAINT "UQ_e42cf55faeafdcce01a82d24849" UNIQUE (key); + +ALTER TABLE ONLY public.meter_interactions + ADD CONSTRAINT meter_interactions_order_id_key UNIQUE (order_id); + +ALTER TABLE ONLY public.meter_interactions + ADD CONSTRAINT meter_interactions_pkey PRIMARY KEY (id); + +ALTER TABLE ONLY public.pd_site_submissions + ADD CONSTRAINT pd_public_submissions_pkey PRIMARY KEY (id); + +ALTER TABLE ONLY public.pd_sites + ADD CONSTRAINT pd_sites_pkey PRIMARY KEY (id); diff --git a/docs/plans/002-oss-migration/002b-task5-generation/sections/07-fk-constraints-body.sql b/docs/plans/002-oss-migration/002b-task5-generation/sections/07-fk-constraints-body.sql new file mode 100644 index 0000000..6597ba0 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/sections/07-fk-constraints-body.sql @@ -0,0 +1,287 @@ +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "FK_04e1a755d5b760f681e5205557d" FOREIGN KEY (dcu_id) REFERENCES public.dcus(id); + +ALTER TABLE ONLY public.mppts + ADD CONSTRAINT "FK_0a681c1d6d0a67331bbc6956427" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "FK_0b03d1bc1cff784570333129e63" FOREIGN KEY (historical_grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.transactions + ADD CONSTRAINT "FK_0b171330be0cb621f8d73b87a9e" FOREIGN KEY (wallet_id) REFERENCES public.wallets(id); + +ALTER TABLE ONLY public.accounts + ADD CONSTRAINT "FK_13bf998d703e7d4c7b9a90f4f76" FOREIGN KEY (supabase_id) REFERENCES auth.users(id); + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_18c521072a6ca9d57365ffb24fe" FOREIGN KEY (agent_id) REFERENCES public.agents(id); + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_236a383b1007cf64f0c8c7f6534" FOREIGN KEY (customer_id) REFERENCES public.customers(id); + +ALTER TABLE ONLY public.accounts + ADD CONSTRAINT "FK_238d61e0f8ac37278f726efac20" FOREIGN KEY (organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.dcus + ADD CONSTRAINT "FK_254c3db39098da62eb3f96f0006" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.poles + ADD CONSTRAINT "FK_2c21d74185234b0dd39588a4d36" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_2d52a7fc85cfbb0421af977766e" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_2fc5f64b328675b5203d54c7929" FOREIGN KEY (connection_id) REFERENCES public.connections(id); + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_336b627b254b70b0702436e6aff" FOREIGN KEY (author_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "FK_35239534a89f0d6dab2612645eb" FOREIGN KEY (author_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY public.notes + ADD CONSTRAINT "FK_35b89a50cb9203dccff44136519" FOREIGN KEY (author_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY public.connections + ADD CONSTRAINT "FK_37f79eb1e29a53cc582fbb805e0" FOREIGN KEY (customer_id) REFERENCES public.customers(id); + +ALTER TABLE ONLY public.ussd_sessions + ADD CONSTRAINT "FK_3a2606011b8d9f1de07de2bf0a8" FOREIGN KEY (meter_id) REFERENCES public.meters(id); + +ALTER TABLE ONLY public.routers + ADD CONSTRAINT "FK_3c3ddc58369a9a8147776e39f40" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.transactions + ADD CONSTRAINT "FK_3cb0558ed36997f1d9ecc1118e7" FOREIGN KEY (order_id) REFERENCES public.orders(id); + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "FK_3f770c153b3333ad1d29cfbf784" FOREIGN KEY (meter_id) REFERENCES public.meters(id); + +ALTER TABLE ONLY public.grids + ADD CONSTRAINT "FK_433ef6a589cd535dff42b34612e" FOREIGN KEY (organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_4a300197465db92edfc5563d20b" FOREIGN KEY (meter_id) REFERENCES public.meters(id); + +ALTER TABLE ONLY public.ussd_sessions + ADD CONSTRAINT "FK_4cad92626a62976ac0b49244d2e" FOREIGN KEY (bank_id) REFERENCES public.banks(id); + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "FK_4eb64f5decb7250cfa993410c1e" FOREIGN KEY (last_meter_commissioning_id) REFERENCES public.meter_commissionings(id) ON DELETE SET NULL; + +ALTER TABLE ONLY public.customers + ADD CONSTRAINT "FK_51a88ee1d4fceb047e7cfda3baa" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "FK_5606d6ec5ab568377509edc5267" FOREIGN KEY (last_encountered_issue_id) REFERENCES public.issues(id) ON DELETE SET NULL; + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "FK_576b0e9af0bec469bff33b965aa" FOREIGN KEY (receiver_wallet_id) REFERENCES public.wallets(id); + +ALTER TABLE ONLY public.metering_hardware_imports + ADD CONSTRAINT "FK_5c2bb31b2e45c3f1da0b72071a2" FOREIGN KEY (metering_hardware_install_session_id) REFERENCES public.metering_hardware_install_sessions(id); + +ALTER TABLE ONLY public.meter_command_batches + ADD CONSTRAINT "FK_5c44fae05dc443720f62664e563" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.notes + ADD CONSTRAINT "FK_5ec9536f852f1923097ba1ecdac" FOREIGN KEY (meter_id) REFERENCES public.meters(id); + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "FK_60816230893327daacc86ab41c8" FOREIGN KEY (author_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "FK_6580899a2293de27787376887fa" FOREIGN KEY (customer_id) REFERENCES public.customers(id); + +ALTER TABLE ONLY public.meter_commissionings + ADD CONSTRAINT "FK_6684f0790c54ed3d441f78df91a" FOREIGN KEY (metering_hardware_install_session_id) REFERENCES public.metering_hardware_install_sessions(id); + +ALTER TABLE ONLY public.meter_command_batches + ADD CONSTRAINT "FK_6e26cb6e966f484f0f897127c84" FOREIGN KEY (author_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY public.meter_command_batch_executions + ADD CONSTRAINT "FK_7b0737005e7c358392d87ebb329" FOREIGN KEY (meter_command_batch_id) REFERENCES public.meter_command_batches(id); + +ALTER TABLE ONLY public.issues + ADD CONSTRAINT "FK_7b15f3fbbedf51a1a3d21583b7e" FOREIGN KEY (meter_id) REFERENCES public.meters(id); + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "FK_86d4557a79e374b5c55cb3b66d8" FOREIGN KEY (last_metering_hardware_install_session_id) REFERENCES public.metering_hardware_install_sessions(id) ON DELETE SET NULL; + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "FK_8db1f4e4f8122bd25d50ad96b26" FOREIGN KEY (meter_id) REFERENCES public.meters(id); + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "FK_8f12fe9c7b078122adcae80375d" FOREIGN KEY (sender_wallet_id) REFERENCES public.wallets(id); + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "FK_92081072063eaf51300ab6c267d" FOREIGN KEY (ussd_session_id) REFERENCES public.ussd_sessions(id); + +ALTER TABLE ONLY public.dcus + ADD CONSTRAINT "FK_9334b587e6e5291f8ccd1b11c44" FOREIGN KEY (last_metering_hardware_install_session_id) REFERENCES public.metering_hardware_install_sessions(id) ON DELETE SET NULL; + +ALTER TABLE ONLY public.notifications + ADD CONSTRAINT "FK_982f77636cb1da8e76745949530" FOREIGN KEY (notification_parameter_id) REFERENCES public.notification_parameters(id); + +ALTER TABLE ONLY public.agents + ADD CONSTRAINT "FK_9bb7cb7efc780ec4d3dec34354f" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "FK_a11871575f7ddb95e567d842bc6" FOREIGN KEY (pole_id) REFERENCES public.poles(id); + +ALTER TABLE ONLY public.notifications + ADD CONSTRAINT "FK_a1ec3b4b4f2017665b534e60256" FOREIGN KEY (account_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY public.members + ADD CONSTRAINT "FK_a9da2a35fdd2cd46a7bfc57fd73" FOREIGN KEY (busy_commissioning_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_abde505f056e2f46c5df2c12491" FOREIGN KEY (organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.energy_cabins + ADD CONSTRAINT "FK_ba81ffc1506025a549849f06f36" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "FK_be64a09dc738420a409cb960264" FOREIGN KEY (last_metering_hardware_import_id) REFERENCES public.metering_hardware_imports(id) ON DELETE SET NULL; + +ALTER TABLE ONLY public.ussd_session_hops + ADD CONSTRAINT "FK_bf89299bd6534528adad645b7a5" FOREIGN KEY (ussd_session_id) REFERENCES public.ussd_sessions(id); + +ALTER TABLE ONLY public.notifications + ADD CONSTRAINT "FK_c1053eec8005016c7d9febdc484" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.notifications + ADD CONSTRAINT "FK_cb7b1fb018b296f2107e998b2ff" FOREIGN KEY (organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "FK_d281c6e5f391b8de5b221032da6" FOREIGN KEY (connection_id) REFERENCES public.connections(id); + +ALTER TABLE ONLY public.connection_requested_meters + ADD CONSTRAINT "FK_d390197863e9a88be5e122d9695" FOREIGN KEY (connection_id) REFERENCES public.connections(id); + +ALTER TABLE ONLY public.api_keys + ADD CONSTRAINT "FK_decaf331589778e33441b2a8d9e" FOREIGN KEY (account_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY public.agents + ADD CONSTRAINT "FK_df520decc9e003a843d8edd9867" FOREIGN KEY (account_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY public.notes + ADD CONSTRAINT "FK_e04c36c14bc9f01f84cd7655b68" FOREIGN KEY (customer_id) REFERENCES public.customers(id); + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_e3f3409859562fbe10b78d1399e" FOREIGN KEY (member_id) REFERENCES public.members(id); + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "FK_e63e504d8e35ef37a2c56b75eb9" FOREIGN KEY (connection_id) REFERENCES public.connections(id); + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_eb582acfd9d83af268d328e0b79" FOREIGN KEY (dcu_id) REFERENCES public.dcus(id); + +ALTER TABLE ONLY public.customers + ADD CONSTRAINT "FK_ebcc29963874e55053e8ee80be5" FOREIGN KEY (account_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY public.ussd_sessions + ADD CONSTRAINT "FK_ede50d58fa445bfa012135249fd" FOREIGN KEY (account_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY public.notes + ADD CONSTRAINT "FK_f1a5ea7b77453030e2a1df27479" FOREIGN KEY (connection_id) REFERENCES public.connections(id); + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "FK_f3c53f4b922a011aa53d9e0b1ad" FOREIGN KEY (dcu_id) REFERENCES public.dcus(id); + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "FK_f499c61c6d6a0ac3f794d966edc" FOREIGN KEY (organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "FK_f5782e05e8688f0cfbb5c4a52ce" FOREIGN KEY (agent_id) REFERENCES public.agents(id); + +ALTER TABLE ONLY public.members + ADD CONSTRAINT "FK_fd9dfb97e21b75fc45d42aa614a" FOREIGN KEY (account_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY public.agents + ADD CONSTRAINT agents_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.connection_requested_meters + ADD CONSTRAINT connection_requested_meters_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.connections + ADD CONSTRAINT connections_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.customers + ADD CONSTRAINT customers_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.dcus + ADD CONSTRAINT dcus_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.meter_command_batch_executions + ADD CONSTRAINT meter_command_batch_executions_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.meter_command_batches + ADD CONSTRAINT meter_command_batches_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.energy_cabins + ADD CONSTRAINT energy_cabins_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.issues + ADD CONSTRAINT issues_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.members + ADD CONSTRAINT members_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.meter_commissionings + ADD CONSTRAINT meter_commissionings_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.meter_interactions + ADD CONSTRAINT meter_interactions_batch_execution_id_fkey FOREIGN KEY (batch_execution_id) REFERENCES public.meter_command_batch_executions(id); + +ALTER TABLE ONLY public.meter_interactions + ADD CONSTRAINT meter_interactions_meter_commissioning_id_fkey FOREIGN KEY (meter_commissioning_id) REFERENCES public.meter_commissionings(id); + +ALTER TABLE ONLY public.meter_interactions + ADD CONSTRAINT meter_interactions_meter_id_fkey FOREIGN KEY (meter_id) REFERENCES public.meters(id); + +ALTER TABLE ONLY public.meter_interactions + ADD CONSTRAINT meter_interactions_order_id_fkey FOREIGN KEY (order_id) REFERENCES public.orders(id); + +ALTER TABLE ONLY public.metering_hardware_imports + ADD CONSTRAINT metering_hardware_imports_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT metering_hardware_install_sessions_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT meters_rls_grid_id_fkey FOREIGN KEY (rls_grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT meters_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.mppts + ADD CONSTRAINT mppts_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.notes + ADD CONSTRAINT notes_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT orders_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.pd_site_submissions + ADD CONSTRAINT pd_site_submissions_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.pd_sites + ADD CONSTRAINT pd_sites_operations_grid_id_fkey FOREIGN KEY (operations_grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.pd_sites + ADD CONSTRAINT pd_sites_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.poles + ADD CONSTRAINT poles_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.routers + ADD CONSTRAINT routers_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.transactions + ADD CONSTRAINT transactions_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT wallets_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); diff --git a/docs/plans/002-oss-migration/002b-task5-generation/sections/08-indexes-body.sql b/docs/plans/002-oss-migration/002b-task5-generation/sections/08-indexes-body.sql new file mode 100644 index 0000000..6d80a7d --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/sections/08-indexes-body.sql @@ -0,0 +1,87 @@ +CREATE INDEX "IDX_14f6e0badc019bbdd2f66f7e8a" ON public.notifications USING btree (notification_status); + +CREATE UNIQUE INDEX "IDX_36d092cd2d89756f62421011bb" ON public.meters USING btree (external_reference, external_system); + +CREATE INDEX "IDX_5c820aa56824815f8e19484ff5" ON public.transactions USING btree (wallet_id, transaction_status, created_at); + +CREATE UNIQUE INDEX "IDX_9c4f5693f52e6e5e51e1ca05af" ON public.dcus USING btree (external_reference, external_system); + +CREATE INDEX idx_agents_rls_organization_id ON public.agents USING btree (rls_organization_id); + +CREATE INDEX idx_connection_customer_id ON public.connections USING btree (customer_id); + +CREATE INDEX idx_connection_requested_meters_rls_organization_id ON public.connection_requested_meters USING btree (rls_organization_id); + +CREATE INDEX idx_connections_rls_organization_id ON public.connections USING btree (rls_organization_id); + +CREATE INDEX idx_customer_grid_id ON public.customers USING btree (grid_id); + +CREATE INDEX idx_customers_rls_organization_id ON public.customers USING btree (rls_organization_id); + +CREATE INDEX idx_dcus_rls_organization_id ON public.dcus USING btree (rls_organization_id); + +CREATE INDEX idx_meter_command_batch_executions_rls_organization_id ON public.meter_command_batch_executions USING btree (rls_organization_id); + +CREATE INDEX idx_meter_command_batches_rls_organization_id ON public.meter_command_batches USING btree (rls_organization_id); + +CREATE INDEX idx_energy_cabins_location_geom ON public.energy_cabins USING gist (location_geom); + +CREATE INDEX idx_energy_cabins_rls_organization_id ON public.energy_cabins USING btree (rls_organization_id); + +CREATE INDEX idx_issues_open ON public.issues USING btree (rls_organization_id, meter_id, created_at DESC) WHERE (issue_status = 'OPEN'::public.issue_status_enum); + +CREATE INDEX idx_issues_rls_organization_id ON public.issues USING btree (rls_organization_id); + +CREATE INDEX idx_members_rls_organization_id ON public.members USING btree (rls_organization_id); + +CREATE INDEX idx_meter_commissionings_rls_organization_id ON public.meter_commissionings USING btree (rls_organization_id); + +CREATE INDEX idx_meter_connection_id ON public.meters USING btree (connection_id); + +CREATE INDEX idx_meter_dcu_id ON public.meters USING btree (dcu_id); + +CREATE INDEX idx_meter_interaction_batch_execution_id ON public.meter_interactions USING btree (batch_execution_id, meter_interaction_status); + +CREATE INDEX idx_meter_interaction_meter_commissioning_id ON public.meter_interactions USING btree (meter_commissioning_id); + +CREATE INDEX idx_meter_interaction_meter_id_extra ON public.meter_interactions USING btree (meter_id, meter_interaction_type, created_at DESC); + +CREATE INDEX idx_metering_hardware_install_sessions_rls_organization_id ON public.metering_hardware_install_sessions USING btree (rls_organization_id); + +CREATE INDEX idx_meters_rls_organization_id ON public.meters USING btree (rls_organization_id); + +CREATE INDEX idx_mppts_rls_organization_id ON public.mppts USING btree (rls_organization_id); + +CREATE INDEX idx_notes_rls_organization_id ON public.notes USING btree (rls_organization_id); + +CREATE INDEX idx_orders_energy_topup_grid ON public.orders USING btree (historical_grid_id) WHERE (meta_order_type = 'ENERGY_TOPUP'::public.order_type_enum); + +CREATE INDEX idx_orders_energy_topup_receiver ON public.orders USING btree (meta_receiver_id) WHERE (meta_order_type = 'ENERGY_TOPUP'::public.order_type_enum); + +CREATE INDEX idx_orders_historical_grid_id ON public.orders USING btree (historical_grid_id); + +CREATE INDEX idx_orders_meta_receiver_id ON public.orders USING btree (meta_receiver_id); + +CREATE INDEX idx_orders_order_optimized ON public.orders USING btree (updated_at DESC); + +CREATE INDEX idx_orders_rls_organization_id ON public.orders USING btree (rls_organization_id); + +CREATE INDEX idx_orders_rls_sender_receiver ON public.orders USING btree (meta_sender_type, meta_sender_id, meta_receiver_type, meta_receiver_id); + +CREATE INDEX idx_pd_site_submissions_location_geom ON public.pd_site_submissions USING gist (location_geom); + +CREATE INDEX idx_pd_site_submissions_outline_geom ON public.pd_site_submissions USING gist (outline_geom); + +CREATE INDEX idx_pd_sites_location_geom ON public.pd_sites USING gist (location_geom); + +CREATE INDEX idx_pd_sites_outline_geom ON public.pd_sites USING gist (outline_geom); + +CREATE INDEX idx_poles_location_geom ON public.poles USING gist (location_geom); + +CREATE INDEX idx_poles_rls_organization_id ON public.poles USING btree (rls_organization_id); + +CREATE INDEX idx_routers_rls_organization_id ON public.routers USING btree (rls_organization_id); + +CREATE INDEX idx_transactions_rls_organization_id ON public.transactions USING btree (rls_organization_id); + +CREATE INDEX idx_wallets_rls_organization_id ON public.wallets USING btree (rls_organization_id); diff --git a/docs/plans/002-oss-migration/002b-task5-generation/sections/08b-new-indexes.sql b/docs/plans/002-oss-migration/002b-task5-generation/sections/08b-new-indexes.sql new file mode 100644 index 0000000..3829afa --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/sections/08b-new-indexes.sql @@ -0,0 +1,59 @@ +-- New indexes (register #25, #30) — FK/RLS-predicate coverage gaps found while +-- auditing the legacy schema; not present in the legacy reference DB. + +-- register #22 — at most one platform-operator organization (ADR-007 Amendment) +CREATE UNIQUE INDEX one_platform_operator_org ON public.organizations USING btree (organization_type) WHERE (organization_type = 'PLATFORM_OPERATOR'::public.organization_type_enum); + +-- register #25 (Task 3c H1b) +CREATE INDEX idx_accounts_organization_id ON public.accounts USING btree (organization_id); + +-- register #30 Tier 1 — RLS-predicate / renamed-FK gaps +CREATE INDEX idx_grids_organization_id ON public.grids USING btree (organization_id); +CREATE INDEX idx_metering_hardware_imports_rls_organization_id ON public.metering_hardware_imports USING btree (rls_organization_id); +CREATE INDEX idx_meter_command_batch_executions_meter_command_batch_id ON public.meter_command_batch_executions USING btree (meter_command_batch_id); + +-- register #30 Tier 2 — plain FK-column hygiene (44 columns across 22 tables) +CREATE INDEX idx_agents_grid_id ON public.agents USING btree (grid_id); +CREATE INDEX idx_api_keys_account_id ON public.api_keys USING btree (account_id); +CREATE INDEX idx_audits_agent_id ON public.audits USING btree (agent_id); +CREATE INDEX idx_audits_author_id ON public.audits USING btree (author_id); +CREATE INDEX idx_audits_connection_id ON public.audits USING btree (connection_id); +CREATE INDEX idx_audits_customer_id ON public.audits USING btree (customer_id); +CREATE INDEX idx_audits_dcu_id ON public.audits USING btree (dcu_id); +CREATE INDEX idx_audits_grid_id ON public.audits USING btree (grid_id); +CREATE INDEX idx_audits_member_id ON public.audits USING btree (member_id); +CREATE INDEX idx_audits_meter_id ON public.audits USING btree (meter_id); +CREATE INDEX idx_audits_organization_id ON public.audits USING btree (organization_id); +CREATE INDEX idx_connection_requested_meters_connection_id ON public.connection_requested_meters USING btree (connection_id); +CREATE INDEX idx_dcus_grid_id ON public.dcus USING btree (grid_id); +CREATE INDEX idx_energy_cabins_grid_id ON public.energy_cabins USING btree (grid_id); +CREATE INDEX idx_meter_command_batches_author_id ON public.meter_command_batches USING btree (author_id); +CREATE INDEX idx_meter_command_batches_grid_id ON public.meter_command_batches USING btree (grid_id); +CREATE INDEX idx_meter_commissionings_metering_hardware_install_session_id ON public.meter_commissionings USING btree (metering_hardware_install_session_id); +CREATE INDEX idx_metering_hardware_imports_metering_hardware_install_session_id ON public.metering_hardware_imports USING btree (metering_hardware_install_session_id); +CREATE INDEX idx_metering_hardware_install_sessions_author_id ON public.metering_hardware_install_sessions USING btree (author_id); +CREATE INDEX idx_metering_hardware_install_sessions_dcu_id ON public.metering_hardware_install_sessions USING btree (dcu_id); +CREATE INDEX idx_metering_hardware_install_sessions_meter_id ON public.metering_hardware_install_sessions USING btree (meter_id); +CREATE INDEX idx_meters_pole_id ON public.meters USING btree (pole_id); +CREATE INDEX idx_mppts_grid_id ON public.mppts USING btree (grid_id); +CREATE INDEX idx_notes_author_id ON public.notes USING btree (author_id); +CREATE INDEX idx_notes_connection_id ON public.notes USING btree (connection_id); +CREATE INDEX idx_notes_customer_id ON public.notes USING btree (customer_id); +CREATE INDEX idx_notes_meter_id ON public.notes USING btree (meter_id); +CREATE INDEX idx_notifications_account_id ON public.notifications USING btree (account_id); +CREATE INDEX idx_notifications_grid_id ON public.notifications USING btree (grid_id); +CREATE INDEX idx_notifications_notification_parameter_id ON public.notifications USING btree (notification_parameter_id); +CREATE INDEX idx_notifications_organization_id ON public.notifications USING btree (organization_id); +CREATE INDEX idx_orders_author_id ON public.orders USING btree (author_id); +CREATE INDEX idx_orders_receiver_wallet_id ON public.orders USING btree (receiver_wallet_id); +CREATE INDEX idx_orders_sender_wallet_id ON public.orders USING btree (sender_wallet_id); +CREATE INDEX idx_pd_site_submissions_organization_id ON public.pd_site_submissions USING btree (organization_id); +CREATE INDEX idx_pd_sites_operations_grid_id ON public.pd_sites USING btree (operations_grid_id); +CREATE INDEX idx_pd_sites_organization_id ON public.pd_sites USING btree (organization_id); +CREATE INDEX idx_poles_grid_id ON public.poles USING btree (grid_id); +CREATE INDEX idx_routers_grid_id ON public.routers USING btree (grid_id); +CREATE INDEX idx_transactions_order_id ON public.transactions USING btree (order_id); +CREATE INDEX idx_ussd_session_hops_ussd_session_id ON public.ussd_session_hops USING btree (ussd_session_id); +CREATE INDEX idx_ussd_sessions_account_id ON public.ussd_sessions USING btree (account_id); +CREATE INDEX idx_ussd_sessions_bank_id ON public.ussd_sessions USING btree (bank_id); +CREATE INDEX idx_ussd_sessions_meter_id ON public.ussd_sessions USING btree (meter_id); diff --git a/docs/plans/002-oss-migration/002b-task5-generation/sections/09-views-body.sql b/docs/plans/002-oss-migration/002b-task5-generation/sections/09-views-body.sql new file mode 100644 index 0000000..11aeb79 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/sections/09-views-body.sql @@ -0,0 +1,110 @@ +CREATE VIEW public.agents_with_account WITH (security_invoker='true') AS + SELECT agents.id, + agents.created_at, + agents.grid_id, + agents.account_id, + accounts.full_name, + accounts.phone, + accounts.deleted_at + FROM (public.agents + JOIN public.accounts ON ((agents.account_id = accounts.id))); + +CREATE VIEW public.customers_with_account WITH (security_invoker='true') AS + SELECT c.id, + c.created_at, + c.gender, + c.total_connection_fee, + c.total_connection_paid, + c.is_hidden_from_reporting, + c.lives_primarily_in_the_community, + c.latitude, + c.longitude, + c.generator_owned, + c.grid_id, + c.account_id, + a.full_name, + a.phone, + a.deleted_at, + (c.total_connection_paid >= c.total_connection_fee) AS has_fully_paid_connection_fees, + ( SELECT string_agg((m.external_reference)::text, ', '::text) AS string_agg + FROM (public.connections conn + JOIN public.meters m ON ((m.connection_id = conn.id))) + WHERE (conn.customer_id = c.id)) AS meter + FROM (public.customers c + JOIN public.accounts a ON ((c.account_id = a.id))); + +CREATE VIEW public.meters_with_account_and_statuses WITH (security_invoker='true') AS + SELECT m.id, + m.created_at, + m.external_reference, + m.deleted_at, + m.balance, + m.balance_updated_at, + m.kwh_credit_available, + m.kwh_credit_available_updated_at, + m.last_non_zero_consumption_at, + m.is_on, + m.should_be_on, + m.is_on_updated_at, + m.should_be_on_updated_at, + m.is_manual_mode_on, + m.is_manual_mode_on_updated_at, + m.voltage, + m.voltage_updated_at, + m.power, + m.power_updated_at, + m.latitude, + m.longitude, + m.coord_accuracy, + m.power_limit, + m.power_limit_updated_at, + m.power_limit_should_be, + m.power_limit_should_be_updated_at, + m.is_starred, + m.external_system, + m.meter_type, + m.nickname, + m.last_seen_at, + m.issue_check_execution_session, + m.issue_check_last_run_at, + m.pole_id, + m.meter_phase, + m.last_metering_hardware_install_session_id, + m.kwh_tariff, + m.version, + m.power_limit_hps_mode, + m.communication_protocol, + m.is_cabin_meter, + m.last_encountered_issue_id, + m.connection_id, + m.dcu_id, + m.decoder_key, + m.last_sts_token_issued_at, + m.rls_grid_id, + m.rls_organization_id, + m.is_test_mode_on, + c.grid_id, + c.is_hidden_from_reporting, + a.full_name, + a.phone, + ( + CASE + WHEN (m.last_metering_hardware_install_session_id IS NULL) THEN 'SUCCESSFUL'::text + WHEN ((i.metering_hardware_import_status = 'PENDING'::public.mhi_status_enum) OR (com.meter_commissioning_status = 'PENDING'::public.meter_commissioning_status_enum)) THEN 'PENDING'::text + WHEN ((i.metering_hardware_import_status = 'PROCESSING'::public.mhi_status_enum) OR (com.meter_commissioning_status = 'PROCESSING'::public.meter_commissioning_status_enum)) THEN 'PROCESSING'::text + WHEN (com.meter_commissioning_status = 'SUCCESSFUL'::public.meter_commissioning_status_enum) THEN 'SUCCESSFUL'::text + WHEN ((i.metering_hardware_import_status = 'FAILED'::public.mhi_status_enum) OR (com.meter_commissioning_status = 'FAILED'::public.meter_commissioning_status_enum) OR ((i.metering_hardware_import_status = 'SUCCESSFUL'::public.mhi_status_enum) AND (com.meter_commissioning_status IS NULL))) THEN 'FAILED'::text + ELSE NULL::text + END)::public.mhi_status_enum AS install_status, + CASE + WHEN (iss.issue_status = 'OPEN'::public.issue_status_enum) THEN iss.issue_type + ELSE NULL::public.issue_type_enum + END AS open_issue + FROM (((((((public.meters m + JOIN public.connections conn ON ((m.connection_id = conn.id))) + JOIN public.customers c ON ((conn.customer_id = c.id))) + JOIN public.accounts a ON ((c.account_id = a.id))) + LEFT JOIN public.metering_hardware_install_sessions install_session ON ((m.last_metering_hardware_install_session_id = install_session.id))) + LEFT JOIN public.metering_hardware_imports i ON ((install_session.last_metering_hardware_import_id = i.id))) + LEFT JOIN public.meter_commissionings com ON ((install_session.last_meter_commissioning_id = com.id))) + LEFT JOIN public.issues iss ON ((m.last_encountered_issue_id = iss.id))); diff --git a/docs/plans/002-oss-migration/002b-task5-generation/sections/10-functions-body.sql b/docs/plans/002-oss-migration/002b-task5-generation/sections/10-functions-body.sql new file mode 100644 index 0000000..e160e68 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/sections/10-functions-body.sql @@ -0,0 +1,444 @@ +-- ============================================================================= +-- Functions +-- ============================================================================= +-- 25 keep functions: 17 carried over from the legacy chain (10 of them with +-- redesigned bodies per register #23), 2 renamed/redesigned (register #16, +-- #22), and 6 new RLS org-lookup leaf/delegate helpers (register #23). +-- Dropped: append_rls_organization_id_by_device_id, +-- append_rls_organization_id_by_receiver_meter_id, +-- append_rls_organization_id_by_historical_grid_id (register #12, #7, #21), +-- lock_next_order, lock_next_pd_action (register #13, #14), +-- notify_make_about_is_fs_on_updated, notify_make_about_is_hps_on_updated, +-- notify_make_about_kwh_tariff_essential_service_updated (register #6, +-- parameterized to the Make-integration operator recipe). + +-- --------------------------------------------------------------------------- +-- RLS org-lookup helpers (register #23 §4) — leaf + delegates, STABLE +-- --------------------------------------------------------------------------- + +CREATE FUNCTION public.rls_org_id_from_grid(grid_id integer) RETURNS integer + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO '' + AS $$ + SELECT organization_id FROM public.grids WHERE id = grid_id; +$$; + +CREATE FUNCTION public.rls_org_id_from_customer(customer_id integer) RETURNS integer + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO '' + AS $$ + SELECT public.rls_org_id_from_grid(grid_id) FROM public.customers WHERE id = customer_id; +$$; + +CREATE FUNCTION public.rls_org_id_from_connection(connection_id integer) RETURNS integer + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO '' + AS $$ + SELECT public.rls_org_id_from_customer(customer_id) FROM public.connections WHERE id = connection_id; +$$; + +CREATE FUNCTION public.rls_org_id_from_agent(agent_id integer) RETURNS integer + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO '' + AS $$ + SELECT public.rls_org_id_from_grid(grid_id) FROM public.agents WHERE id = agent_id; +$$; + +CREATE FUNCTION public.rls_org_id_from_meter(meter_id integer) RETURNS integer + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO '' + AS $$ + SELECT public.rls_org_id_from_connection(connection_id) FROM public.meters WHERE id = meter_id; +$$; + +CREATE FUNCTION public.rls_org_id_from_dcu(dcu_id integer) RETURNS integer + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO '' + AS $$ + SELECT public.rls_org_id_from_grid(grid_id) FROM public.dcus WHERE id = dcu_id; +$$; + +-- --------------------------------------------------------------------------- +-- append_rls_organization_id_by_* trigger functions (register #23 §4) — +-- redesigned to delegate to the helpers above; dead joins removed; +-- SET search_path TO '' added throughout +-- --------------------------------------------------------------------------- + +CREATE FUNCTION public.append_rls_organization_id_by_account_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + SELECT + organization_id INTO NEW.rls_organization_id + FROM + public.accounts + WHERE + accounts.id = NEW.account_id; + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.append_rls_organization_id_by_grid_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + NEW.rls_organization_id := public.rls_org_id_from_grid(NEW.grid_id); + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.append_rls_organization_id_by_connection_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + NEW.rls_organization_id := public.rls_org_id_from_connection(NEW.connection_id); + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.append_rls_organization_id_by_customer_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + NEW.rls_organization_id := public.rls_org_id_from_customer(NEW.customer_id); + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.append_rls_organization_id_by_customer_id_or_agent_id_or_connec() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + IF NEW.customer_id IS NOT NULL THEN + NEW.rls_organization_id := public.rls_org_id_from_customer(NEW.customer_id); + ELSIF NEW.agent_id IS NOT NULL THEN + NEW.rls_organization_id := public.rls_org_id_from_agent(NEW.agent_id); + ELSIF NEW.connection_id IS NOT NULL THEN + NEW.rls_organization_id := public.rls_org_id_from_connection(NEW.connection_id); + ELSIF NEW.organization_id IS NOT NULL THEN + NEW.rls_organization_id := NEW.organization_id; + ELSIF NEW.meter_id IS NOT NULL THEN + NEW.rls_organization_id := public.rls_org_id_from_meter(NEW.meter_id); + END IF; + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.append_rls_organization_id_by_dcu_id_or_meter_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + IF NEW.dcu_id IS NOT NULL THEN + NEW.rls_organization_id := public.rls_org_id_from_dcu(NEW.dcu_id); + ELSIF NEW.meter_id IS NOT NULL THEN + NEW.rls_organization_id := public.rls_org_id_from_meter(NEW.meter_id); + END IF; + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.append_rls_organization_id_by_meter_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + NEW.rls_organization_id := public.rls_org_id_from_meter(NEW.meter_id); + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.append_rls_organization_id_by_metering_hardware_install_session() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +DECLARE + session_meter_id integer; +BEGIN + SELECT + meter_id INTO session_meter_id + FROM + public.metering_hardware_install_sessions + WHERE + id = NEW.metering_hardware_install_session_id; + NEW.rls_organization_id := public.rls_org_id_from_meter(session_meter_id); + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.append_rls_organization_id_by_order_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +DECLARE + order_grid_id integer; +BEGIN + SELECT + historical_grid_id INTO order_grid_id + FROM + public.orders + WHERE + id = NEW.order_id; + NEW.rls_organization_id := public.rls_org_id_from_grid(order_grid_id); + RETURN NEW; +END; +$$; + +-- Renamed from append_rls_organization_id_by_directive_batch_id() (register #16); body redesigned (register #23) +CREATE FUNCTION public.append_rls_organization_id_by_meter_command_batch_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +DECLARE + batch_grid_id integer; +BEGIN + SELECT + grid_id INTO batch_grid_id + FROM + public.meter_command_batches + WHERE + id = NEW.meter_command_batch_id; + NEW.rls_organization_id := public.rls_org_id_from_grid(batch_grid_id); + RETURN NEW; +END; +$$; + +-- --------------------------------------------------------------------------- +-- Admin-organization membership check (register #22) — renamed from +-- rls_check_if_nxt_member(); reads PLATFORM_OPERATOR row directly (indexed +-- via one_platform_operator_org). GUC + ALTER DATABASE dropped: Supabase +-- migrations run as non-superuser postgres and cannot set custom DB params. +-- --------------------------------------------------------------------------- + +CREATE FUNCTION public.rls_check_if_admin_org_member() RETURNS boolean + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO '' + AS $$ + SELECT (auth.jwt () -> 'app_metadata' ->> 'account_type') = 'MEMBER' + AND (auth.jwt () -> 'app_metadata' ->> 'organization_id')::int = ( + SELECT + o.id + FROM + public.organizations AS o + WHERE + o.organization_type = 'PLATFORM_OPERATOR' + ORDER BY + o.id + LIMIT 1); +$$; + +-- --------------------------------------------------------------------------- +-- Remaining RLS helpers — mark STABLE only (register #31), logic unchanged +-- --------------------------------------------------------------------------- + +CREATE FUNCTION public.rls_check_if_lender() RETURNS boolean + LANGUAGE plpgsql STABLE SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + RETURN (auth.jwt () -> 'app_metadata' ->> 'organization_type')::text = 'LENDER'; +END; +$$; + +CREATE FUNCTION public.rls_get_member_org_id() RETURNS integer + LANGUAGE plpgsql STABLE SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + RETURN (auth.jwt () -> 'app_metadata' ->> 'organization_id')::int; +END; +$$; + +-- --------------------------------------------------------------------------- +-- auth.users trigger functions (H2 keep, unchanged) +-- --------------------------------------------------------------------------- + +CREATE FUNCTION public.handle_new_user() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + -- RAISE LOG 'New user: %', NEW; + -- Every Supabase User gets an Account that holds more metadata + -- We don't have access to raw_app_meta_data here but we do in + -- the subsequent update trigger + INSERT INTO public.accounts (supabase_id, email, phone, full_name, telegram_link_token) + VALUES(NEW.id, NEW.email, NEW.phone, NEW.raw_user_meta_data ->> 'full_name', gen_random_uuid ()); + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.handle_update_user() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + UPDATE + public.accounts + SET + email = NEW.email, + phone = NEW.phone, + full_name = NEW.raw_user_meta_data ->> 'full_name', + organization_id = CAST(COALESCE(NEW.raw_app_meta_data ->> 'organization_id', NULL) AS integer) + WHERE + supabase_id = NEW.id; + RETURN NEW; +END; +$$; + +-- --------------------------------------------------------------------------- +-- Payments / production-monitoring RPCs +-- --------------------------------------------------------------------------- + +-- register #24: SET search_path TO '' added; logic unchanged +CREATE FUNCTION public.lock_next_order_and_wallets(uuid uuid) RETURNS TABLE(id integer) + LANGUAGE plpgsql + SET search_path TO '' + AS $$ +DECLARE + order_id_var integer; + sender_wallet_id_var integer; + receiver_wallet_id_var integer; +BEGIN + -- 1. Atomic Selection + SELECT + _orders.id, + _orders.sender_wallet_id, + _orders.receiver_wallet_id + INTO + order_id_var, + sender_wallet_id_var, + receiver_wallet_id_var + FROM + public.orders _orders + JOIN public.wallets AS sender_wallets ON sender_wallets.id = _orders.sender_wallet_id + JOIN public.wallets AS receiver_wallets ON receiver_wallets.id = _orders.receiver_wallet_id + WHERE + _orders.order_status = 'PENDING' + AND sender_wallets.lock_session IS NULL + AND receiver_wallets.lock_session IS NULL + ORDER BY + _orders.id ASC + LIMIT 1 + FOR UPDATE OF _orders, sender_wallets, receiver_wallets SKIP LOCKED; + + -- 2. Early exit if no order matches + IF order_id_var IS NULL THEN + RETURN; + END IF; + + -- 3. Update the Order + -- explicit alias 'order_record' prevents confusion with any variables + UPDATE + public.orders AS order_record + SET + lock_session = uuid + WHERE + order_record.id = order_id_var; + + -- 4. Update the Wallets and Return their IDs + -- explicit alias 'wallet_record' prevents confusion + RETURN QUERY + UPDATE + public.wallets AS wallet_record + SET + lock_session = uuid + WHERE + wallet_record.id IN (sender_wallet_id_var, receiver_wallet_id_var) + RETURNING + wallet_record.id; +END; +$$; + +-- register #27: mark STABLE; logic unchanged +CREATE FUNCTION public.find_energy_topup_revenue(grid_id integer, start_date timestamp with time zone, end_date timestamp with time zone) RETURNS numeric + LANGUAGE plpgsql STABLE + SET search_path TO '' + AS $$ +DECLARE + total_revenue NUMERIC; +BEGIN + SELECT + SUM(amount) INTO total_revenue + FROM + public.orders + WHERE + meta_order_type = 'ENERGY_TOPUP'::public.order_type_enum + AND order_status = 'COMPLETED'::public.order_status_enum + AND meta_is_hidden_from_reporting = FALSE + AND historical_grid_id = grid_id + AND created_at >= start_date + AND created_at < end_date; + RETURN total_revenue; +END; +$$; + +-- register #27: mark STABLE; GROUP BY meta_receiver_id dropped (customer-level +-- top-spender aggregation; the legacy meter id in GROUP BY split one customer +-- across rows) — return shape unchanged +CREATE FUNCTION public.find_top_spenders(grid_id integer, limit_count integer, start_date timestamp with time zone, end_date timestamp with time zone) RETURNS TABLE(full_name character varying, id integer, amount double precision) + LANGUAGE plpgsql STABLE + SET search_path TO '' + AS $$ +BEGIN + RETURN QUERY + SELECT + meta_receiver_name_part_2 AS full_name, + meta_receiver_id_part_2 AS id, + SUM( + _orders.amount +) AS amount + FROM + public.orders AS _orders + WHERE + meta_order_type = 'ENERGY_TOPUP'::public.order_type_enum + AND order_status = 'COMPLETED'::public.order_status_enum + AND meta_is_hidden_from_reporting = FALSE + AND historical_grid_id = grid_id + AND created_at >= start_date + AND created_at < end_date + GROUP BY + meta_receiver_id_part_2, + meta_receiver_name_part_2 + ORDER BY + SUM( + _orders.amount +) DESC + LIMIT limit_count; +END; +$$; + +-- register #26: are_all_dcus_online / are_all_dcus_under_high_load_threshold +-- dropped from RETURNS TABLE and SELECT body (grids columns dropped, register +-- #17); mark STABLE. Pegasus gateway alerts use dcus.is_online instead. +CREATE FUNCTION public.get_grid_status(grid_id integer) RETURNS TABLE(is_hps_on boolean, is_fs_on boolean, is_cabin_meter_credit_depleting boolean, customer_count integer) + LANGUAGE plpgsql STABLE + SET search_path TO '' + AS $$BEGIN + RETURN QUERY + SELECT + _grid.is_hps_on, + _grid.is_fs_on, + _grid.is_cabin_meter_credit_depleting, + ( + SELECT + COUNT(DISTINCT _customer.id)::integer + FROM + public.customers _customer + JOIN public.accounts _account ON _account.id = _customer.account_id + JOIN public.connections _connection ON _connection.customer_id = _customer.id + JOIN public.meters _meter ON _meter.connection_id = _connection.id + WHERE + _customer.grid_id = _grid.id + AND _customer.is_hidden_from_reporting = FALSE + AND _account.deleted_at IS NULL) AS customer_count + FROM + public.grids _grid + WHERE + _grid.id = grid_id; +END;$$; diff --git a/docs/plans/002-oss-migration/002b-task5-generation/sections/11-triggers-body.sql b/docs/plans/002-oss-migration/002b-task5-generation/sections/11-triggers-body.sql new file mode 100644 index 0000000..10bf582 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/sections/11-triggers-body.sql @@ -0,0 +1,41 @@ +CREATE TRIGGER append_rls_organization_id_on_agent_insert BEFORE INSERT ON public.agents FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + +CREATE TRIGGER append_rls_organization_id_on_connection_insert BEFORE INSERT ON public.connections FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_customer_id(); + +CREATE TRIGGER append_rls_organization_id_on_customer_insert BEFORE INSERT ON public.customers FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + +CREATE TRIGGER append_rls_organization_id_on_dcus_insert BEFORE INSERT ON public.dcus FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + +CREATE TRIGGER append_rls_organization_id_on_meter_command_batch_execution_insert BEFORE INSERT ON public.meter_command_batch_executions FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_meter_command_batch_id(); + +CREATE TRIGGER append_rls_organization_id_on_meter_command_batch_insert BEFORE INSERT ON public.meter_command_batches FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + +CREATE TRIGGER append_rls_organization_id_on_energy_cabin_insert BEFORE INSERT ON public.energy_cabins FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + +CREATE TRIGGER append_rls_organization_id_on_issue_insert BEFORE INSERT ON public.issues FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_meter_id(); + +CREATE TRIGGER append_rls_organization_id_on_member_insert BEFORE INSERT ON public.members FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_account_id(); + +CREATE TRIGGER append_rls_organization_id_on_meter_commissioning_insert BEFORE INSERT ON public.meter_commissionings FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_metering_hardware_install_session(); + +CREATE TRIGGER append_rls_organization_id_on_meter_insert BEFORE INSERT ON public.meters FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_connection_id(); + +CREATE TRIGGER append_rls_organization_id_on_meter_install_session_insert BEFORE INSERT ON public.metering_hardware_install_sessions FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_dcu_id_or_meter_id(); + +CREATE TRIGGER append_rls_organization_id_on_mppt_insert BEFORE INSERT ON public.mppts FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + +CREATE TRIGGER append_rls_organization_id_on_note_insert BEFORE INSERT ON public.notes FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_customer_id(); + +CREATE TRIGGER append_rls_organization_id_on_pole_insert BEFORE INSERT ON public.poles FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + +CREATE TRIGGER append_rls_organization_id_on_requested_connection_meters_inser BEFORE INSERT ON public.connection_requested_meters FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_connection_id(); + +CREATE TRIGGER append_rls_organization_id_on_router_insert BEFORE INSERT ON public.routers FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + +CREATE TRIGGER append_rls_organization_id_on_transaction_insert BEFORE INSERT ON public.transactions FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_order_id(); + +CREATE TRIGGER append_rls_organization_id_on_wallet_insert BEFORE INSERT ON public.wallets FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_customer_id_or_agent_id_or_connec(); + +CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION public.handle_new_user(); + +CREATE TRIGGER on_auth_user_updated AFTER UPDATE ON auth.users FOR EACH ROW EXECUTE FUNCTION public.handle_update_user(); diff --git a/docs/plans/002-oss-migration/002b-task5-generation/sections/12-grants-body.sql b/docs/plans/002-oss-migration/002b-task5-generation/sections/12-grants-body.sql new file mode 100644 index 0000000..90810dc --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/sections/12-grants-body.sql @@ -0,0 +1,396 @@ +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 SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.accounts TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.accounts TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.accounts TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.agents TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.agents TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.agents TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.agents_with_account TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.agents_with_account TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.agents_with_account TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.api_keys TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.api_keys TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.api_keys TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.audits TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.audits TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.audits TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.banks TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.banks TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.banks TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.dcus TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.dcus TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.dcus TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meters TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meters TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meters TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.poles TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.poles TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.poles TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.connection_requested_meters TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.connection_requested_meters TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.connection_requested_meters TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.connections TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.connections TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.connections TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.customers TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.customers TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.customers TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.customers_with_account TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.customers_with_account TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.customers_with_account TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_command_batch_executions TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_command_batch_executions TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_command_batch_executions TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_command_batches TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_command_batches TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_command_batches TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.energy_cabins TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.energy_cabins TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.energy_cabins TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.grids TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.grids TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.grids TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.issues TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.issues TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.issues TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.members TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.members TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.members TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_commissionings TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_commissionings TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_commissionings TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_interactions TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_interactions TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_interactions TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.metering_hardware_imports TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.metering_hardware_imports TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.metering_hardware_imports TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.metering_hardware_install_sessions TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.metering_hardware_install_sessions TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.metering_hardware_install_sessions TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meters_with_account_and_statuses TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meters_with_account_and_statuses TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meters_with_account_and_statuses TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.mppts TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.mppts TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.mppts TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notes TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notes TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notes TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notification_parameters TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notification_parameters TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notification_parameters TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notifications TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notifications TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notifications TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.orders TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.orders TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.orders TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.organizations TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.organizations TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.organizations TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_site_submissions TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_site_submissions TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_site_submissions TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_sites TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_sites TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_sites TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.routers TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.routers TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.routers TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.solcast_cache TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.solcast_cache TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.solcast_cache TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.transactions TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.transactions TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.transactions TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.ussd_session_hops TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.ussd_session_hops TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.ussd_session_hops TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.ussd_sessions TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.ussd_sessions TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.ussd_sessions TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.wallets TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.wallets TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.wallets TO service_role; + +GRANT ALL ON SEQUENCE public.accounts_id_seq TO anon; +GRANT ALL ON SEQUENCE public.accounts_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.accounts_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.agents_id_seq TO anon; +GRANT ALL ON SEQUENCE public.agents_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.agents_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.api_keys_id_seq TO anon; +GRANT ALL ON SEQUENCE public.api_keys_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.api_keys_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.audits_id_seq TO anon; +GRANT ALL ON SEQUENCE public.audits_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.audits_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.banks_id_seq TO anon; +GRANT ALL ON SEQUENCE public.banks_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.banks_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.connection_requested_meters_id_seq TO anon; +GRANT ALL ON SEQUENCE public.connection_requested_meters_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.connection_requested_meters_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.connections_id_seq TO anon; +GRANT ALL ON SEQUENCE public.connections_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.connections_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.customers_id_seq TO anon; +GRANT ALL ON SEQUENCE public.customers_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.customers_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.dcus_id_seq TO anon; +GRANT ALL ON SEQUENCE public.dcus_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.dcus_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.meter_command_batch_executions_id_seq TO anon; +GRANT ALL ON SEQUENCE public.meter_command_batch_executions_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.meter_command_batch_executions_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.meter_command_batches_id_seq TO anon; +GRANT ALL ON SEQUENCE public.meter_command_batches_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.meter_command_batches_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.energy_cabins_id_seq TO anon; +GRANT ALL ON SEQUENCE public.energy_cabins_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.energy_cabins_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.grids_id_seq TO anon; +GRANT ALL ON SEQUENCE public.grids_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.grids_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.issues_id_seq TO anon; +GRANT ALL ON SEQUENCE public.issues_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.issues_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.members_id_seq TO anon; +GRANT ALL ON SEQUENCE public.members_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.members_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.meter_commissionings_id_seq TO anon; +GRANT ALL ON SEQUENCE public.meter_commissionings_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.meter_commissionings_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.meter_interactions_id_seq TO anon; +GRANT ALL ON SEQUENCE public.meter_interactions_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.meter_interactions_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.metering_hardware_imports_id_seq TO anon; +GRANT ALL ON SEQUENCE public.metering_hardware_imports_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.metering_hardware_imports_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.metering_hardware_install_sessions_id_seq TO anon; +GRANT ALL ON SEQUENCE public.metering_hardware_install_sessions_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.metering_hardware_install_sessions_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.meters_id_seq TO anon; +GRANT ALL ON SEQUENCE public.meters_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.meters_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.mppts_id_seq TO anon; +GRANT ALL ON SEQUENCE public.mppts_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.mppts_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.notes_id_seq TO anon; +GRANT ALL ON SEQUENCE public.notes_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.notes_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.notification_parameters_id_seq TO anon; +GRANT ALL ON SEQUENCE public.notification_parameters_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.notification_parameters_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.notifications_id_seq TO anon; +GRANT ALL ON SEQUENCE public.notifications_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.notifications_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.orders_id_seq TO anon; +GRANT ALL ON SEQUENCE public.orders_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.orders_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.organizations_id_seq TO anon; +GRANT ALL ON SEQUENCE public.organizations_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.organizations_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.pd_site_submissions_id_seq TO anon; +GRANT ALL ON SEQUENCE public.pd_site_submissions_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.pd_site_submissions_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.pd_sites_id_seq TO anon; +GRANT ALL ON SEQUENCE public.pd_sites_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.pd_sites_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.poles_id_seq TO anon; +GRANT ALL ON SEQUENCE public.poles_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.poles_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.routers_id_seq TO anon; +GRANT ALL ON SEQUENCE public.routers_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.routers_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.solcast_cache_id_seq TO anon; +GRANT ALL ON SEQUENCE public.solcast_cache_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.solcast_cache_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.transactions_id_seq TO anon; +GRANT ALL ON SEQUENCE public.transactions_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.transactions_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.ussd_session_hops_id_seq TO anon; +GRANT ALL ON SEQUENCE public.ussd_session_hops_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.ussd_session_hops_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.ussd_sessions_id_seq TO anon; +GRANT ALL ON SEQUENCE public.ussd_sessions_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.ussd_sessions_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.wallets_id_seq TO anon; +GRANT ALL ON SEQUENCE public.wallets_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.wallets_id_seq TO service_role; + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_account_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_account_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_account_id() TO service_role; + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_connection_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_connection_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_connection_id() TO service_role; + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_customer_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_customer_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_customer_id() TO service_role; + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_customer_id_or_agent_id_or_connec() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_customer_id_or_agent_id_or_connec() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_customer_id_or_agent_id_or_connec() TO service_role; + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_dcu_id_or_meter_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_dcu_id_or_meter_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_dcu_id_or_meter_id() TO service_role; + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_meter_command_batch_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_meter_command_batch_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_meter_command_batch_id() TO service_role; + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_grid_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_grid_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_grid_id() TO service_role; + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_meter_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_meter_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_meter_id() TO service_role; + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_metering_hardware_install_session() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_metering_hardware_install_session() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_metering_hardware_install_session() TO service_role; + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_order_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_order_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_order_id() TO service_role; + +GRANT ALL ON FUNCTION public.find_energy_topup_revenue(grid_id integer, start_date timestamp with time zone, end_date timestamp with time zone) TO anon; +GRANT ALL ON FUNCTION public.find_energy_topup_revenue(grid_id integer, start_date timestamp with time zone, end_date timestamp with time zone) TO authenticated; +GRANT ALL ON FUNCTION public.find_energy_topup_revenue(grid_id integer, start_date timestamp with time zone, end_date timestamp with time zone) TO service_role; + +GRANT ALL ON FUNCTION public.find_top_spenders(grid_id integer, limit_count integer, start_date timestamp with time zone, end_date timestamp with time zone) TO anon; +GRANT ALL ON FUNCTION public.find_top_spenders(grid_id integer, limit_count integer, start_date timestamp with time zone, end_date timestamp with time zone) TO authenticated; +GRANT ALL ON FUNCTION public.find_top_spenders(grid_id integer, limit_count integer, start_date timestamp with time zone, end_date timestamp with time zone) TO service_role; + +GRANT ALL ON FUNCTION public.get_grid_status(grid_id integer) TO anon; +GRANT ALL ON FUNCTION public.get_grid_status(grid_id integer) TO authenticated; +GRANT ALL ON FUNCTION public.get_grid_status(grid_id integer) TO service_role; + +GRANT ALL ON FUNCTION public.handle_new_user() TO anon; +GRANT ALL ON FUNCTION public.handle_new_user() TO authenticated; +GRANT ALL ON FUNCTION public.handle_new_user() TO service_role; + +GRANT ALL ON FUNCTION public.handle_update_user() TO anon; +GRANT ALL ON FUNCTION public.handle_update_user() TO authenticated; +GRANT ALL ON FUNCTION public.handle_update_user() TO service_role; + +GRANT ALL ON FUNCTION public.lock_next_order_and_wallets(uuid uuid) TO anon; +GRANT ALL ON FUNCTION public.lock_next_order_and_wallets(uuid uuid) TO authenticated; +GRANT ALL ON FUNCTION public.lock_next_order_and_wallets(uuid uuid) TO service_role; + +GRANT ALL ON FUNCTION public.rls_check_if_lender() TO anon; +GRANT ALL ON FUNCTION public.rls_check_if_lender() TO authenticated; +GRANT ALL ON FUNCTION public.rls_check_if_lender() TO service_role; + +GRANT ALL ON FUNCTION public.rls_check_if_admin_org_member() TO anon; +GRANT ALL ON FUNCTION public.rls_check_if_admin_org_member() TO authenticated; +GRANT ALL ON FUNCTION public.rls_check_if_admin_org_member() TO service_role; + +GRANT ALL ON FUNCTION public.rls_get_member_org_id() TO anon; +GRANT ALL ON FUNCTION public.rls_get_member_org_id() TO authenticated; +GRANT ALL ON FUNCTION public.rls_get_member_org_id() TO service_role; + +GRANT ALL ON FUNCTION public.rls_org_id_from_grid(integer) TO anon; +GRANT ALL ON FUNCTION public.rls_org_id_from_grid(integer) TO authenticated; +GRANT ALL ON FUNCTION public.rls_org_id_from_grid(integer) TO service_role; + +GRANT ALL ON FUNCTION public.rls_org_id_from_customer(integer) TO anon; +GRANT ALL ON FUNCTION public.rls_org_id_from_customer(integer) TO authenticated; +GRANT ALL ON FUNCTION public.rls_org_id_from_customer(integer) TO service_role; + +GRANT ALL ON FUNCTION public.rls_org_id_from_connection(integer) TO anon; +GRANT ALL ON FUNCTION public.rls_org_id_from_connection(integer) TO authenticated; +GRANT ALL ON FUNCTION public.rls_org_id_from_connection(integer) TO service_role; + +GRANT ALL ON FUNCTION public.rls_org_id_from_agent(integer) TO anon; +GRANT ALL ON FUNCTION public.rls_org_id_from_agent(integer) TO authenticated; +GRANT ALL ON FUNCTION public.rls_org_id_from_agent(integer) TO service_role; + +GRANT ALL ON FUNCTION public.rls_org_id_from_meter(integer) TO anon; +GRANT ALL ON FUNCTION public.rls_org_id_from_meter(integer) TO authenticated; +GRANT ALL ON FUNCTION public.rls_org_id_from_meter(integer) TO service_role; + +GRANT ALL ON FUNCTION public.rls_org_id_from_dcu(integer) TO anon; +GRANT ALL ON FUNCTION public.rls_org_id_from_dcu(integer) TO authenticated; +GRANT ALL ON FUNCTION public.rls_org_id_from_dcu(integer) TO service_role; diff --git a/docs/plans/002-oss-migration/002b-task5-generation/sections/13-policies-body.sql b/docs/plans/002-oss-migration/002b-task5-generation/sections/13-policies-body.sql new file mode 100644 index 0000000..82ab077 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/sections/13-policies-body.sql @@ -0,0 +1,153 @@ +CREATE POLICY "Allow NXT Grid to CRUD" ON public.connections TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to insert" ON public.grids FOR INSERT TO authenticated WITH CHECK (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to insert" ON public.meters FOR INSERT TO authenticated WITH CHECK (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to insert" ON public.notes FOR INSERT TO authenticated WITH CHECK (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to insert" ON public.organizations FOR INSERT TO authenticated WITH CHECK (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to insert" ON public.pd_sites FOR INSERT TO authenticated WITH CHECK (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to insert" ON public.poles FOR INSERT TO authenticated WITH CHECK (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to insert" ON public.wallets FOR INSERT WITH CHECK (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.accounts FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.agents FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.connection_requested_meters FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.connections FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.customers FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.dcus FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.meter_command_batch_executions FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.meter_command_batches FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.energy_cabins FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.grids FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.issues FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.members FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.meter_commissionings FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.meter_interactions FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.metering_hardware_install_sessions FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.meters FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.mppts FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.notes FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.orders FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.organizations FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.pd_sites FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.poles FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.routers FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.transactions FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.wallets FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to update" ON public.accounts FOR UPDATE TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to update" ON public.connections FOR UPDATE TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to update" ON public.grids FOR UPDATE TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to update" ON public.meters FOR UPDATE TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to update" ON public.organizations FOR UPDATE TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to update" ON public.pd_site_submissions FOR UPDATE TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to update" ON public.pd_sites FOR UPDATE TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow authenticated to do everything" ON public.audits USING (true); + +CREATE POLICY "Allow authenticated to select" ON public.metering_hardware_imports FOR SELECT TO authenticated USING (true); + +CREATE POLICY "Allow authors to update their own" ON public.notes FOR UPDATE TO authenticated USING ((( SELECT auth.uid() AS uid) = ( SELECT account.supabase_id + FROM public.accounts account + WHERE (account.id = notes.author_id)))); + +CREATE POLICY "Allow lenders to select" ON public.accounts FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_lender() AS rls_check_if_lender)); + +CREATE POLICY "Allow lenders to select" ON public.customers FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_lender() AS rls_check_if_lender)); + +CREATE POLICY "Allow lenders to select" ON public.grids FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_lender() AS rls_check_if_lender)); + +CREATE POLICY "Allow lenders to select" ON public.orders FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_lender() AS rls_check_if_lender)); + +CREATE POLICY "Allow org member to select" ON public.accounts FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = organization_id)); + +CREATE POLICY "Allow org members to insert" ON public.notes FOR INSERT TO authenticated WITH CHECK (( SELECT (public.rls_get_member_org_id() = rls_organization_id) AS rls_get_member_org_id)); + +CREATE POLICY "Allow org members to insert" ON public.poles FOR INSERT WITH CHECK (( SELECT (public.rls_get_member_org_id() = rls_organization_id) AS rls_get_member_org_id)); + +CREATE POLICY "Allow org members to select" ON public.agents FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.connection_requested_meters FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.connections FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.customers FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.dcus FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.meter_command_batches FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.energy_cabins FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.grids FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = organization_id)); + +CREATE POLICY "Allow org members to select" ON public.issues FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.members FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.meter_commissionings FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.meter_interactions FOR SELECT TO authenticated USING (true); + +CREATE POLICY "Allow org members to select" ON public.metering_hardware_install_sessions FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.mppts FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.notes FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.orders FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.organizations FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = id)); + +CREATE POLICY "Allow org members to select" ON public.poles FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.routers FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.transactions FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select (TEMPORARILY OPEN)" ON public.wallets FOR SELECT TO authenticated USING (true); + +CREATE POLICY "Allow org members to select own and UNASSIGNED" ON public.meters FOR SELECT TO authenticated USING (((rls_organization_id IS NULL) OR (( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id))); + +CREATE POLICY "Allow org members to update" ON public.accounts FOR UPDATE TO authenticated USING (( SELECT (public.rls_get_member_org_id() = organization_id) AS rls_get_member_org_id)); + +CREATE POLICY "Allow org members to update" ON public.orders FOR UPDATE TO authenticated USING (( SELECT (public.rls_get_member_org_id() = rls_organization_id) AS rls_get_member_org_id)); + +CREATE POLICY "Allow public selects (temporary for sending response to MAKE)" ON public.pd_site_submissions FOR SELECT USING (true); + +CREATE POLICY "Allow public writes (Sites can be submitted publicly)" ON public.pd_site_submissions FOR INSERT WITH CHECK (true); diff --git a/docs/plans/002-oss-migration/002b-task5-generation/sections/14-rls-enable-body.sql b/docs/plans/002-oss-migration/002b-task5-generation/sections/14-rls-enable-body.sql new file mode 100644 index 0000000..e9e5445 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/sections/14-rls-enable-body.sql @@ -0,0 +1,35 @@ +ALTER TABLE public.accounts ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.agents ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.api_keys ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.audits ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.banks ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.connection_requested_meters ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.connections ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.customers ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.dcus ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.meter_command_batch_executions ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.meter_command_batches ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.energy_cabins ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.grids ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.issues ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.members ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.meter_commissionings ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.meter_interactions ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.metering_hardware_imports ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.metering_hardware_install_sessions ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.meters ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.mppts ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.notes ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.notification_parameters ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.notifications ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.orders ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.organizations ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.pd_site_submissions ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.pd_sites ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.poles ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.routers ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.solcast_cache ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.transactions ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.ussd_session_hops ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.ussd_sessions ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.wallets ENABLE ROW LEVEL SECURITY; diff --git a/docs/plans/002-oss-migration/002b-task5-generation/sections/preamble.sql b/docs/plans/002-oss-migration/002b-task5-generation/sections/preamble.sql new file mode 100644 index 0000000..2deea54 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/sections/preamble.sql @@ -0,0 +1,19 @@ +-- +-- PostgreSQL database dump +-- + +-- Dumped from database version 15.8 +-- Dumped by pg_dump version 17.0 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET transaction_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; + diff --git a/docs/plans/002-oss-migration/002b-task5-generation/verify_types.py b/docs/plans/002-oss-migration/002b-task5-generation/verify_types.py new file mode 100644 index 0000000..ca9ca29 --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task5-generation/verify_types.py @@ -0,0 +1,43 @@ +import json, re +from registry import DROP_ENUMS, EXTERNAL_SYSTEM_ENUM_DROP_VALUES, NOTIFICATION_TYPE_ENUM_DROP_VALUES +from pathlib import Path + +BASE = Path(__file__).parent + +blocks = json.load(open(BASE / "blocks.json")) +enums = [b for b in blocks if b["type"] == "TYPE"] + +kept_enums = {} +for b in enums: + name = b["name"] + if name in DROP_ENUMS: + continue + m = re.search(r"CREATE TYPE public\.\w+ AS ENUM \(\n(.*?)\n\);", b["body"], re.S) + values = [v.strip().strip("'").strip(",").strip("'") for v in m.group(1).split("\n")] + values = [v.strip().strip("'") for v in m.group(1).split(",\n")] + values = [v.strip().lstrip("'").rstrip("'").strip() for v in values] + kept_enums[name] = values + +# Apply trims +kept_enums["external_system_enum"] = [v for v in kept_enums["external_system_enum"] if v not in EXTERNAL_SYSTEM_ENUM_DROP_VALUES] +kept_enums["notification_type_enum"] = [v for v in kept_enums["notification_type_enum"] if v not in NOTIFICATION_TYPE_ENUM_DROP_VALUES] +kept_enums["organization_type_enum"].append("PLATFORM_OPERATOR") + +# Now parse my authored file and compare +authored_text = open(BASE / "sections" / "02-types.sql").read() +authored_blocks = re.findall(r"CREATE TYPE public\.(\w+) AS ENUM \(\n(.*?)\n\);", authored_text, re.S) +authored = {} +for name, body in authored_blocks: + values = [v.strip().lstrip("'").rstrip("'").strip().rstrip(",").strip("'") for v in body.split(",\n")] + authored[name] = values + +print("Enum count expected (kept):", len(kept_enums)) +print("Enum count authored:", len(authored)) +print("Missing from authored:", set(kept_enums) - set(authored)) +print("Extra in authored:", set(authored) - set(kept_enums)) +mismatches = 0 +for name in kept_enums: + if name in authored and kept_enums[name] != authored[name]: + mismatches += 1 + print(f"MISMATCH {name}: expected={kept_enums[name]} authored={authored[name]}") +print("Total mismatches:", mismatches) diff --git a/docs/plans/002-oss-migration/002b-task6/compare_schemas.py b/docs/plans/002-oss-migration/002b-task6/compare_schemas.py new file mode 100644 index 0000000..66b093b --- /dev/null +++ b/docs/plans/002-oss-migration/002b-task6/compare_schemas.py @@ -0,0 +1,518 @@ +#!/usr/bin/env python3 +"""Task 6 A/B schema comparison: legacy dump vs new baseline dump. + +Parses both pg_dump outputs, normalizes the legacy side per registry.py +(renames, drops), then classifies every structural difference against the +deviation register. Reports unexplained hunks. + +Usage (from repo root, after dumps exist): + python3 docs/plans/002-oss-migration/002b-task6/compare_schemas.py \\ + /tmp/schema-old.sql /tmp/schema-new.sql +""" +import re +import sys +from collections import defaultdict +from pathlib import Path + +REGISTRY_DIR = Path(__file__).resolve().parent.parent / "002b-task5-generation" +sys.path.insert(0, str(REGISTRY_DIR)) + +from registry import ( # noqa: E402 + COLUMN_CHANGES, + DROP_ENUMS, + DROP_FUNCTIONS, + DROP_TABLES, + DROP_TRIGGERS, + DROP_VIEWS, + EXTERNAL_SYSTEM_ENUM_DROP_VALUES, + KEEP_VIEWS, + NOTIFICATION_TYPE_ENUM_DROP_VALUES, + PARAMETERIZE_ROLES, + RENAME_FUNCTIONS, + RENAME_SEQUENCES, + RENAME_TABLES, + RENAME_TRIGGERS, + VIEW_COLUMN_CHANGES, +) + +HEADER_RE = re.compile( + r"^--\n-- Name: (?P.*?); Type: (?P.*?); Schema: (?P.*?); Owner: (?P.*?)\n--\n", + re.MULTILINE, +) + +FK_HARDEN = { + ("meters", "last_metering_hardware_install_session_id"): "SET NULL", + ("metering_hardware_install_sessions", "last_metering_hardware_import_id"): "SET NULL", + ("metering_hardware_install_sessions", "last_meter_commissioning_id"): "SET NULL", + ("dcus", "last_metering_hardware_install_session_id"): "SET NULL", + ("meters", "last_encountered_issue_id"): "SET NULL", +} + + +def parse_blocks(text): + matches = list(HEADER_RE.finditer(text)) + blocks = [] + for i, m in enumerate(matches): + start = m.end() + end = matches[i + 1].start() if i + 1 < len(matches) else len(text) + blocks.append({ + "name": m.group("name"), + "type": m.group("type"), + "schema": m.group("schema"), + "body": text[start:end].rstrip("\n"), + }) + return blocks + + +def parse_table_columns(body): + m = re.search(r"CREATE TABLE public\.\w+ \((.*)\);", body, re.DOTALL) + if not m: + return [] + cols = [] + for line in m.group(1).split("\n"): + line = line.strip().rstrip(",") + if not line or line.startswith("CONSTRAINT"): + continue + cm = re.match(r"(\w+)\s+", line) + if cm: + cols.append(cm.group(1)) + return cols + + +def parse_enum_values(body): + m = re.search(r"AS ENUM \((.*)\);", body, re.DOTALL) + if not m: + return [] + return re.findall(r"'([^']+)'", m.group(1)) + + +def table_of_policy_block_name(name): + return name.split(" ", 1)[0] + + +def policy_name_of_block(name): + return name.split(" ", 1)[1] + + +def normalize_policy_role(body): + for role in PARAMETERIZE_ROLES: + if f"TO {role}" in body: + return role + if "TO authenticated" in body: + return "authenticated" + if "TO anon" in body: + return "anon" + return "other" + + +def fk_on_delete(body): + if "ON DELETE SET NULL" in body: + return "SET NULL" + if "ON DELETE CASCADE" in body: + return "CASCADE" + return "NO ACTION" + + +def extract_fks(blocks): + fks = {} + for b in blocks: + if b["type"] != "FK CONSTRAINT": + continue + table = b["name"].split(" ", 1)[0] + m = re.search( + r'ADD CONSTRAINT "?(?P[^"]+)"? FOREIGN KEY \((?P[^)]+)\) ' + r'REFERENCES public\.(?P\w+)\((?P[^)]+)\)(?P[^;]*)', + b["body"], + ) + if not m: + continue + cols = [c.strip() for c in m.group("cols").split(",")] + key = (table, tuple(cols)) + fks[key] = { + "table": table, + "columns": cols, + "ref_table": m.group("ref"), + "ref_columns": [c.strip() for c in m.group("refcols").split(",")], + "on_delete": fk_on_delete(b["body"]), + "constraint": m.group("cname"), + } + return fks + + +def extract_indexes(blocks): + return {b["name"]: b["body"].strip() for b in blocks if b["type"] == "INDEX"} + + +def extract_functions(blocks): + return {b["name"].split("(")[0]: b["body"] for b in blocks if b["type"] == "FUNCTION"} + + +def extract_triggers(blocks): + tr = {} + for b in blocks: + if b["type"] != "TRIGGER": + continue + table = b["name"].split(" ", 1)[0] + tname = b["name"][len(table) + 1 :] + tr[(table, tname)] = b["body"].strip() + return tr + + +def extract_policies(blocks): + pol = {} + for b in blocks: + if b["type"] != "POLICY": + continue + table = table_of_policy_block_name(b["name"]) + pname = policy_name_of_block(b["name"]) + role = normalize_policy_role(b["body"]) + pol[(table, pname, role)] = b["body"].strip() + return pol + + +def rename_table(name): + return RENAME_TABLES.get(name, name) + + +def legacy_table_set(tables): + out = set() + for t in tables: + if t in DROP_TABLES: + continue + out.add(rename_table(t)) + return out + + +def legacy_columns(table, cols): + t = rename_table(table) + changes = COLUMN_CHANGES.get(t, {"drop": [], "rename": {}}) + rename_map = changes.get("rename", {}) + drop = set(changes.get("drop", [])) + result = set() + for c in cols: + if c in drop: + continue + result.add(rename_map.get(c, c)) + return result + + +def load_schema(path): + text = path.read_text() + blocks = parse_blocks(text) + tables = {} + enums = {} + views = {} + for b in blocks: + if b["type"] == "TABLE" and b["schema"] == "public": + tables[b["name"]] = parse_table_columns(b["body"]) + elif b["type"] == "TYPE" and b["schema"] == "public": + enums[b["name"]] = parse_enum_values(b["body"]) + elif b["type"] == "VIEW" and b["schema"] == "public": + views[b["name"]] = b["body"] + return { + "blocks": blocks, + "tables": tables, + "enums": enums, + "views": views, + "fks": extract_fks(blocks), + "indexes": extract_indexes(blocks), + "functions": extract_functions(blocks), + "triggers": extract_triggers(blocks), + "policies": extract_policies(blocks), + } + + +def main(): + old_path = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("/tmp/schema-old.sql") + new_path = Path(sys.argv[2]) if len(sys.argv) > 2 else Path("/tmp/schema-new.sql") + + old = load_schema(old_path) + new = load_schema(new_path) + + unexplained = [] + explained = [] + + def explain(msg, register): + explained.append((register, msg)) + + def unexplain(msg): + unexplained.append(msg) + + old_view_names = set(old["views"]) + new_view_names = set(new["views"]) + + only_old = {t for t in old["tables"] if t in DROP_TABLES or t in DROP_VIEWS} + for t in sorted(only_old): + reg = ( + "#1" if t in {"directives", "lorawan_directives"} else + "#7" if t == "meter_credit_transfers" else + "#8" if t in {"features", "member_feature"} else + "#9" if t == "migrations" else + "#12" if t in {"devices", "device_types", "device_logs"} else + "#13" if t in {"payouts", "bank_accounts"} else + "#14" if t.startswith("pd_") and t not in {"pd_sites", "pd_site_submissions"} else + "#15" if t == "autopilot_executions" else + "#4" if t == "directive_watchdog_sessions" else "?" + ) + explain(f"table dropped in baseline: {t}", reg) + + for old_name, new_name in RENAME_TABLES.items(): + if old_name in old["tables"] and new_name in new["tables"]: + explain(f"table renamed: {old_name} → {new_name}", "#16") + elif old_name in old["tables"]: + unexplain(f"rename expected but missing: {old_name} → {new_name}") + + if DROP_VIEWS & old_view_names: + for v in DROP_VIEWS: + explain(f"view dropped: {v}", "#1") + + kept_old_norm = legacy_table_set(old["tables"]) | (KEEP_VIEWS & new_view_names) + new_tables = set(new["tables"]) + missing_in_new = kept_old_norm - new_tables - new_view_names + extra_in_new = (new_tables | new_view_names) - kept_old_norm + for t in sorted(missing_in_new): + unexplain(f"kept table/view missing in new dump: {t}") + for t in sorted(extra_in_new): + unexplain(f"unexpected extra table/view in new dump: {t}") + + for old_t, old_cols in old["tables"].items(): + if old_t in DROP_TABLES: + continue + new_t = rename_table(old_t) + if new_t not in new["tables"]: + continue + old_norm = legacy_columns(old_t, old_cols) + new_cols = set(new["tables"][new_t]) + if old_norm != new_cols: + only_old_cols = old_norm - new_cols + only_new_cols = new_cols - old_norm + expected_drops = set(COLUMN_CHANGES.get(new_t, {}).get("drop", [])) + expected_renames = COLUMN_CHANGES.get(new_t, {}).get("rename", {}) + for c in only_old_cols: + if c in expected_drops or c in expected_renames: + explain(f"column drop/rename on {new_t}: {c}", "column adjustments") + else: + unexplain(f"column only in old (after norm) {new_t}.{c}") + for c in only_new_cols: + if c in expected_renames.values(): + explain(f"column rename target on {new_t}: {c}", "column adjustments") + else: + unexplain(f"column only in new {new_t}.{c}") + + for en, vals in old["enums"].items(): + if en in DROP_ENUMS: + explain(f"enum dropped: {en}", "#1/#7/#13/#14") + continue + if en not in new["enums"]: + unexplain(f"enum missing in new: {en}") + continue + old_vals = set(vals) + new_vals = set(new["enums"][en]) + if en == "external_system_enum": + for v in EXTERNAL_SYSTEM_ENUM_DROP_VALUES: + if v in vals: + explain(f"external_system_enum value dropped: {v}", "#28") + old_vals -= EXTERNAL_SYSTEM_ENUM_DROP_VALUES + if en == "notification_type_enum": + for v in NOTIFICATION_TYPE_ENUM_DROP_VALUES: + if v in vals: + explain(f"notification_type_enum value dropped: {v}", "#29") + old_vals -= NOTIFICATION_TYPE_ENUM_DROP_VALUES + if en == "organization_type_enum" and "PLATFORM_OPERATOR" in new_vals - old_vals: + explain("organization_type_enum value added: PLATFORM_OPERATOR", "#22") + old_vals.add("PLATFORM_OPERATOR") + if old_vals != new_vals: + unexplain( + f"enum value mismatch {en}: old-only={old_vals - new_vals} " + f"new-only={new_vals - old_vals}" + ) + + for en in new["enums"]: + if en not in old["enums"] and en not in DROP_ENUMS: + unexplain(f"unexpected new enum: {en}") + + old_fns = set(old["functions"]) + new_fns = set(new["functions"]) + for fn in DROP_FUNCTIONS: + if fn in old_fns and fn not in new_fns: + reg = ( + "#12" if "device" in fn else + "#7" if "receiver_meter" in fn else + "#21" if "historical_grid" in fn else + "#13" if fn == "lock_next_order" else + "#14" if fn == "lock_next_pd_action" else + "#6" + ) + explain(f"function dropped: {fn}", reg) + for old_fn, new_fn in RENAME_FUNCTIONS.items(): + if old_fn in old_fns and new_fn in new_fns: + explain(f"function renamed: {old_fn} → {new_fn}", "#16/#22") + elif old_fn in old_fns: + unexplain(f"function rename incomplete: {old_fn} → {new_fn}") + new_helpers = { + f for f in new_fns + if f.startswith("rls_org_id_from_") + } + for fn in new_helpers: + if fn not in old_fns: + explain(f"new function: {fn}", "#22/#23") + + for fn in new_fns: + if fn in DROP_FUNCTIONS: + continue + old_fn = fn + for o, n in RENAME_FUNCTIONS.items(): + if n == fn: + old_fn = o + if old_fn not in old_fns: + continue + if old["functions"][old_fn].strip() != new["functions"][fn].strip(): + reg = ( + "#23" if fn.startswith("append_rls_") else + "#22" if fn == "rls_check_if_admin_org_member" else + "#24" if fn == "lock_next_order_and_wallets" else + "#26" if fn == "get_grid_status" else + "#27" if fn in {"find_energy_topup_revenue", "find_top_spenders"} else + "#31" if fn in {"rls_check_if_lender", "rls_get_member_org_id"} else + "body diff" + ) + explain(f"function body changed: {fn}", reg) + + old_idx = set(old["indexes"]) + new_idx = set(new["indexes"]) + only_new_idx = new_idx - old_idx + only_old_idx = old_idx - new_idx + for name in sorted(only_new_idx): + if name.startswith("idx_") and name not in old_idx: + explain(f"new index: {name}", "#25/#30") + for old_name, new_name in RENAME_TABLES.items(): + for idx in list(only_old_idx): + if old_name in idx: + new_equiv = idx.replace(old_name, new_name) + if new_equiv in new_idx: + explain(f"index renamed with table: {idx} → {new_equiv}", "#16") + only_old_idx.discard(idx) + for idx in list(only_old_idx): + if any(dt in idx for dt in DROP_TABLES): + explain(f"index on dropped object: {idx}", "drop cascade") + only_old_idx.discard(idx) + if "one_platform_operator_org" in new_idx or any( + "one_platform_operator" in k for k in new_idx + ): + explain("partial unique index one_platform_operator_org", "#22") + for idx in sorted(only_old_idx): + unexplain(f"index only in old: {idx}") + for idx in sorted(only_new_idx): + if not any(x in idx for x in ["idx_", "one_platform"]): + unexplain(f"index only in new (unattributed): {idx}") + + def norm_fks(fks, side): + out = {} + for (table, cols), fk in fks.items(): + t = rename_table(table) if side == "old" else table + col_list = list(cols) + if side == "old": + changes = COLUMN_CHANGES.get(t, {}).get("rename", {}) + col_list = [changes.get(c, c) for c in col_list] + drop = set(COLUMN_CHANGES.get(t, {}).get("drop", [])) + if any(c in drop for c in col_list): + continue + ref = rename_table(fk["ref_table"]) if side == "old" else fk["ref_table"] + key = (t, tuple(col_list), ref) + out[key] = fk["on_delete"] + return out + + old_fks_n = norm_fks(old["fks"], "old") + new_fks_n = norm_fks(new["fks"], "new") + for key, on_del in new_fks_n.items(): + table, cols, ref = key + harden_key = (table, cols[0]) if len(cols) == 1 else None + if harden_key in FK_HARDEN: + if on_del == "SET NULL": + explain(f"FK ON DELETE SET NULL: {table}.{cols[0]} → {ref}", "#34") + else: + unexplain(f"FK expected SET NULL but got {on_del}: {table}.{cols[0]}") + for key in set(old_fks_n) - set(new_fks_n): + t = key[0] + col = key[1][0] + drops = COLUMN_CHANGES.get(t, {}).get("drop", []) + if col in drops: + explain(f"FK dropped with column: {t}.{col}", "column adjustments") + elif t in DROP_TABLES or any(x in t for x in ["directives", "devices", "payouts"]): + explain(f"FK on dropped scope: {key}", "register drop") + else: + unexplain(f"FK in old only: {key} on_delete={old_fks_n[key]}") + + def filter_policies(pol, side): + out = {} + for (table, pname, role), body in pol.items(): + if role in PARAMETERIZE_ROLES: + continue + t = rename_table(table) if side == "old" else table + if t in DROP_TABLES or table in DROP_TABLES: + continue + if table in DROP_VIEWS: + continue + out[(t, pname, role)] = body + return out + + old_pol = filter_policies(old["policies"], "old") + new_pol = filter_policies(new["policies"], "new") + for key in set(old_pol) - set(new_pol): + t, pname, _role = key + reg = "#2" if "Grafana" in pname else "#1/#7/#8/#12/#13/#14/#15 drop" + explain(f"policy dropped: {t} / {pname}", reg) + for key in set(new_pol) - set(old_pol): + unexplain(f"policy only in new: {key}") + for key in set(old_pol) & set(new_pol): + if old_pol[key] != new_pol[key]: + explain(f"policy body changed: {key[0]} / {key[1]}", "#32/#22") + + old_tr = {} + for (table, tname), body in old["triggers"].items(): + if (tname, table) in DROP_TRIGGERS or table in DROP_TABLES: + continue + t = rename_table(table) + tn = RENAME_TRIGGERS.get(tname, tname) + old_tr[(t, tn)] = body + new_tr = dict(new["triggers"]) + for key in set(old_tr) - set(new_tr): + unexplain(f"trigger in old only: {key}") + for key in set(new_tr) - set(old_tr): + if key[0] == "auth.users" or "auth.users" in new_tr[key]: + explain(f"auth.users trigger (hand-authored): {key}", "legacy chain") + else: + unexplain(f"trigger only in new: {key}") + + print("=" * 72) + print("TASK 6 SCHEMA COMPARISON SUMMARY") + print("=" * 72) + print(f"Old dump: {old_path} ({old_path.stat().st_size // 1024}K)") + print(f"New dump: {new_path} ({new_path.stat().st_size // 1024}K)") + print() + print(f"Explained differences: {len(explained)}") + by_reg = defaultdict(list) + for reg, msg in explained: + by_reg[reg].append(msg) + for reg in sorted(by_reg, key=lambda x: (not x.startswith("#"), x)): + print(f"\n--- {reg} ({len(by_reg[reg])}) ---") + for msg in by_reg[reg][:8]: + print(f" • {msg}") + if len(by_reg[reg]) > 8: + print(f" … and {len(by_reg[reg]) - 8} more") + + print(f"\n{'=' * 72}") + print(f"UNEXPLAINED differences: {len(unexplained)}") + print("=" * 72) + if unexplained: + for msg in unexplained[:50]: + print(f" ! {msg}") + if len(unexplained) > 50: + print(f" … and {len(unexplained) - 50} more") + return 1 + print(" (none — all structural diffs map to register entries)") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/docs/plans/002-oss-migration/002c-scaffold-and-pipeline.md b/docs/plans/002-oss-migration/002c-scaffold-and-pipeline.md index 52a04e0..446e401 100644 --- a/docs/plans/002-oss-migration/002c-scaffold-and-pipeline.md +++ b/docs/plans/002-oss-migration/002c-scaffold-and-pipeline.md @@ -5,9 +5,9 @@ plan builds the skeleton), ADR-008 (Phase 1: prove the golden path on hello-world before any domain code lands) **Created:** 2026-07-08 -**Status:** Not started -**Depends on:** 002a (repo restructure) complete. Runs in **parallel** with 002b (database -baseline); Task 8 is the interlock where the two tracks join. +**Status:** Completed (2026-07-14) +**Depends on:** 002a (repo restructure) complete. Ran in **parallel** with 002b (database +baseline); **002b interlock reached** (Tasks 4–5 + Task 8 sign-off). **Execution model:** collaborative — the maintainer may execute tasks manually with the agent advising, or the agent may execute under maintainer review. Ask which mode applies before starting a task; see "Division of labor" in the parent plan. (Existing per-task `Executor:` @@ -28,7 +28,7 @@ mechanism. | Concern | Decision | |---|---| -| Monorepo tool | Nx **23.0.1** (or latest 23.x patch at execution time — record in decisions log), full-modern layout: Project Crystal inferred targets + TS-solution/package-based | +| Monorepo tool | Nx **23.0.2** (or latest 23.x patch at execution time — record in decisions log), full-modern layout: Project Crystal inferred targets + TS-solution/package-based | | Package naming | `@nxt/` scoped packages, pnpm `workspace:*`, imports via package names (no path aliases) | | Package manager | pnpm, Corepack-pinned via `packageManager`; **`pnpm-lock.yaml` committed** | | Node | 24 LTS on all pinning surfaces (`.nvmrc`, `engines`, `packageManager`, CI, Docker) | @@ -37,22 +37,75 @@ mechanism. | Deploy baseline | DO App Platform building from a GitHub branch (no graph awareness — accepted) | | Dropped hacks | `npm-force-resolutions`, `resolutions`, gitignored lockfile, `fix-node-cpu` — zero forward weight, not ported | -## Current state snapshot (2026-07-08 — verify before executing) - -- After 002a: repo root contains `docs/`, `legacy/`, `.github/`, `.gitignore`, `.cursor/`, - `.vscode/`, `AGENTS.md`, license/contributor files — and `supabase/` once 002b Task 4 has - run. No workspace files at root. +## Current state snapshot (2026-07-14 — 002c complete) + +- **Workspace live at root:** Nx 23.0.2 + pnpm 11.12.0 + Node 24. Projects: `apps/api` + (`@nxt/api`), `apps/worker` (`@nxt/worker`), `libs/core` (`@nxt/core`). `pnpm-lock.yaml` + tracked. `legacy/` excluded from graph via `.nxignore`. +- **Hello-world:** `api` serves `GET /health` (name+version from `@nxt/core`); `worker` logs a + heartbeat via `@nestjs/schedule`, no HTTP. `nx run-many -t lint typecheck build test -p + api,worker,core` is green (`test` now runs real jest specs on `core`; still `nx:noop` on + api/worker until real tests land there). +- **Config skeleton (Task 3):** `@nxt/core` exports `getConfig`/`setConfig`/`loadConfig`/ + `requireEnv` from `src/config/`; both apps call `loadConfig()` in `main.ts` before importing + `AppModule` (dynamic `import()`, required so a static import doesn't evaluate the module's + capability-composition decorator before config loads) and compose + `imports: [ ...alwaysOn, ...demoModules(getConfig()) ]`. Root `config.example.json` / + `config.default.json` ship the current schema; the default is copied into each app's + `dist/` via a webpack asset entry so a bare build boots in evaluation mode with zero env. + Temporary `demo` capability in `@nxt/core` proves the three Tier-1 honesty behaviors — see + the decisions log for what's deferred (adminOrganizationId/systemWalletId sourcing, + `NXT_CONFIG_URL`, demo removal trigger). +- **Layout:** legacy-aligned `src/modules/` (no `src/app/`); `@nxt/core` exports from + `src/modules/platform/` and `src/modules/demo/`. Root `package.json` is tooling-only; runtime + deps live per app/lib. +- **Supabase (Task 4):** root `supabase/` chain from 002b (`config.toml`: Postgres 17, API + `54321`, DB `54322`); CLI pinned `2.109.1` at root with `pnpm supabase` script; + `docs/deployment/supabase.md` updated. **Local start verified:** `pnpm supabase start` from + repo root boots the stack; `20260710120000_init` applied; Postgres 17.6 reachable on + `127.0.0.1:54322`; API on `127.0.0.1:54321`. +- **Type-gen pipeline (Task 5):** `pnpm generate-types:local` at root + (`gen-types-local` → `gen-better-types`). Invocation per 002b Task 7: + `pnpm supabase gen types typescript --local --schema public`. Post-process: + `better-supabase-types@2.7.6` (`--enumAsType`) → `supabase/scripts/fix-supabase-json-type.js` + → eslint-fix → `libs/core/src/types/supabase-types.ts` (committed). Intermediate + `supabase/generated-types.ts` gitignored. Golden-path consumption: `@nxt/api` imports + `OrganizationTypeEnum` from `@nxt/core/types/supabase-types` (package subpath export in + `libs/core/package.json` — not a barrel re-export; mirrors legacy `@core/types/supabase-types` + ergonomics without tsconfig path aliases). `nx run-many -t typecheck -p api,core` green. +- **Docker (Task 6):** root `Dockerfile` + `.dockerignore`. Parameterized + `docker build --build-arg APP=api|worker .` verified — both images boot on default config; + `api` `/health` answers from inside the container. Runtime uses Nx prune + hoisted prod + `pnpm install` in `apps/${APP}/dist` (not `pnpm deploy` — see decisions log [6]). +- **CI (Task 7):** `.github/workflows/ci.yml` + `.github/CODEOWNERS` (`supabase/**` + `@bobbybol`). Triggers on PR/push to **`oss-migration`** (switch to `main` at landing). + Active job: **`nx-affected-checks`** — checkout → Node 24 + Corepack → frozen install → + `nrwl/nx-set-shas@v5` → `nx affected -t lint test typecheck build --parallel=3`. Verified + green on push. **`supabase-type-filter` / `supabase-type-drift`** implemented but **commented + out** during migration (verified once on CI; uses `pnpm supabase db start`, not full stack — + see decisions log [7]). GitHub Actions on Node 24-native majors (`checkout@v6`, + `setup-node@v6`, `paths-filter@v4`, `nx-set-shas@v5`). - Legacy stack (reference only, in `legacy/`): Nx 21.2.2, npm, Node 22, webpack, path-alias imports (`@core`, `@tiamat`, `@helpers`), `.eslintrc`-era config referenced from `nx.json`. -- Legacy root `.gitignore` ignores `package-lock.json` (the old hack). The new workspace needs - `pnpm-lock.yaml` **tracked** — fix the ignore rules in Task 1. -- The legacy type-gen pipeline (to be re-established in Task 5): - `supabase gen types typescript --local` → `better-supabase-types … --enumAsType` → - `fix-supabase-json-type.js` → eslint-fix → output `libs/core/src/types/supabase-types.ts`. - Source scripts: `legacy/package.json` + `legacy/.scripts/fix-supabase-json-type.js`. + Legacy type-gen reference: `legacy/package.json` scripts + + `legacy/.scripts/fix-supabase-json-type.js`. - CI trigger note: the migration lives on the **`oss-migration`** branch (roadmap assumption 6). Workflows trigger on PRs targeting that branch; `nrwl/nx-set-shas` gets - `main-branch-name: oss-migration`. Both switch to `main` when the branch lands. + `main-branch-name: oss-migration`. Both switch to `main` when the branch lands. See Task 7 / + decisions log [7] for the type-drift guard deferral. +- **Interlock (Task 8):** clean-clone golden path verified by maintainer — `pnpm install` → + `pnpm supabase start` → `pnpm generate-types:local` (no diff on `supabase-types.ts`) → + `nx run-many -t typecheck build -p api,worker,core` all green. Roadmap interlock reached; + capability imports (002d…) unblocked. +- **DO deploy (Task 9):** App Platform buildpack baseline on branch **`oss-migration`** — two + components (`api` Web Service, `worker` Worker). Build: `corepack enable` + frozen install + + `nx sync` + `nx build`. Run: `node apps/{api,worker}/dist/main.js`. App-wide env: + `NODE_ENV=production`, `NX_DAEMON=false` (build). Public `/health` verified; worker heartbeat + in logs. Runbook: `docs/deployment/digital-ocean-buildpack.md`. +- **Git hooks (Task 10):** husky **9.1.7** + lint-staged **17.0.8** at root; `prepare`: + `"husky"`. Pre-commit: `lint-staged` (ESLint block-only on staged `**/*.ts` via + `--max-warnings=0 --no-warn-ignored`; `legacy/**` ignored via ESLint config) then + `nx affected -t typecheck --uncommitted`. CI remains the authority for full affected lane. ## Non-goals (deferred per ADR-006/007 — do not build) @@ -69,14 +122,14 @@ mechanism. ## Task 1 — Scaffold the workspace at root -- [ ] **Status:** Not started +- [x] **Status:** Complete (2026-07-14) - **Depends on:** 002a complete `create-nx-workspace` requires an empty directory; the repo root is not. Standard workaround: generate into a temp dir, then move contents into the root. ```bash -npx create-nx-workspace@23.0.1 nxt-backend \ +npx create-nx-workspace@23.0.2 nxt-backend \ --preset=ts --package-manager=pnpm --no-cloud # exact flags: check the 23.x docs for the # TS-solution preset name at execution time # then move everything from the temp dir into the repo root (including dotfiles), @@ -101,7 +154,7 @@ Then pin all surfaces: ## Task 2 — Generate hosts and core lib (hello-world) -- [ ] **Status:** Not started +- [x] **Status:** Complete (2026-07-14) - **Depends on:** Task 1 1. `nx g @nx/nest:application apps/api` and `nx g @nx/nest:application apps/worker` — accept @@ -117,11 +170,19 @@ Then pin all surfaces: **Done when:** `nx serve api` and `nx serve worker` both run; `nx run-many -t lint test typecheck build` is green; the boundary probe behaved as expected. +**Webpack + pnpm note (recorded):** Nx's default `externalDependencies: 'all'` reads only the +workspace-root `node_modules/`. Under pnpm, app runtime deps (e.g. `@nestjs/common`) live under +`apps//node_modules/`, so webpack bundled all of Nest and failed on optional peers. Fix: +each host's `webpack.config.js` uses `webpack-node-externals` with `modulesFromFile` pointing +at that app's `package.json`, plus `externalDependencies: 'none'` and `mergeExternals: true` on +`NxAppWebpackPlugin`. No need to restore Nest optional peers at root unless those features are +used. + --- ## Task 3 — ADR-007 config skeleton in `@nxt/core` -- [ ] **Status:** Not started +- [x] **Status:** Complete (2026-07-14) - **Depends on:** Task 2 Build the config mechanism exactly as decided in ADR-007 (read it in full before this task): @@ -148,6 +209,12 @@ Build the config mechanism exactly as decided in ADR-007 (read it in full before build so a bare clone boots in evaluation mode. 6. **Tests:** unit tests for precedence order, schema-version rejection, freeze, and `getConfig()`-before-load error; `setConfig(testConfig)` as the test override pattern. +7. **`deployment.adminOrganizationId` (register #22 / ADR-007 Amendment):** the platform + operator org is **DB-native** — not a static JSON fact. Before closing this task, **decide and + record** how the config layer exposes it: boot-time DB query into `getConfig()`, drop from + Zod schema and query `organizations` at call sites, or keep schema field with a documented + override path for eval mode only. Parent roadmap assumption **#9**. Do not ship hello-world + with an implied JSON-sourced admin org id without recording the choice. **Done when:** both apps boot with zero env (default config); `NXT_CONFIG_JSON` overrides it; the demo capability demonstrates all three honesty behaviors; tests green. @@ -156,7 +223,7 @@ the demo capability demonstrates all three honesty behaviors; tests green. ## Task 4 — Root Supabase chain usable from the workspace -- [ ] **Status:** Not started +- [x] **Status:** Complete (2026-07-14) - **Depends on:** Task 1; coordinates with 002b (Task 4 there creates root `supabase/`) - If 002b has created root `supabase/`: use it as-is (do not wait for its baseline to be @@ -175,7 +242,7 @@ root chain, and the CLI version is lockfile-pinned. ## Task 5 — Re-establish the type-generation pipeline -- [ ] **Status:** Not started +- [x] **Status:** Complete (2026-07-14) - **Depends on:** Tasks 2, 4 Port the legacy pipeline (reference: `legacy/package.json` scripts, @@ -184,10 +251,11 @@ Port the legacy pipeline (reference: `legacy/package.json` scripts, 1. `gen-types-local`: `supabase gen types typescript --local --schema public` (owned schemas only, ADR-004 decision 3 — adopt the exact invocation 002b Task 7 recorded, if available). 2. `gen-better-types`: `better-supabase-types … --enumAsType` → port - `fix-supabase-json-type.js` into the new workspace (e.g. `tools/scripts/`) → eslint-fix → + `fix-supabase-json-type.js` to `supabase/scripts/` → eslint-fix → output **`libs/core/src/types/supabase-types.ts`** (inside `@nxt/core`). -3. Commit the generated file; `@nxt/api` imports one generated type from `@nxt/core` so - typecheck genuinely consumes it (golden-path requirement). +3. Commit the generated file; `@nxt/api` imports one generated type from + `@nxt/core/types/supabase-types` (package subpath export) so typecheck genuinely consumes it + (golden-path requirement). **Done when:** `pnpm generate-types:local` runs end-to-end against the local stack and `nx run-many -t typecheck` is green consuming the committed types. @@ -196,17 +264,18 @@ Port the legacy pipeline (reference: `legacy/package.json` scripts, ## Task 6 — Dockerfile (parameterized, both hosts) -- [ ] **Status:** Not started +- [x] **Status:** Complete (2026-07-14) - **Depends on:** Task 2 Single multi-stage Dockerfile at root, per ADR-006 decision 7: - `ARG APP=api` (api | worker). - **base:** `node:24-slim` + Corepack + pnpm. -- **build:** `pnpm install --frozen-lockfile` (BuildKit cache mount on the pnpm store) → - `nx build ${APP}`. -- **runtime:** `pnpm deploy --prod --filter=@nxt/${APP}` for pruned `node_modules`; copy - `dist/apps/${APP}`; run as the non-root `node` user. No Chromium in any default image. +- **build:** manifest-first `pnpm install --frozen-lockfile` (BuildKit cache mount on the pnpm + store) → `nx sync` → `nx run ${APP}:prune`. +- **runtime:** copy `apps/${APP}/dist` (webpack output + pruned lockfile + + `workspace_modules/`) → `pnpm install --prod --frozen-lockfile --config.node-linker=hoisted` + → run as the non-root `node` user (`node main.js`). No Chromium in any default image. **Done when:** `docker build --build-arg APP=api .` and `…APP=worker .` both build; both containers boot on default config; `api`'s `/health` answers from inside the container. @@ -215,108 +284,118 @@ containers boot on default config; `api`'s `/health` answers from inside the con ## Task 7 — CI: PR checks + schema type-drift lane + CODEOWNERS -- [ ] **Status:** Not started +- [x] **Status:** Complete (2026-07-14) - **Depends on:** Tasks 2, 5 -`.github/workflows/ci.yml` (triggers: `pull_request` targeting `oss-migration`, and `push` to -`oss-migration`; switch to `main` when the branch lands): +`.github/workflows/ci.yml` + `.github/CODEOWNERS` on `oss-migration` (triggers: +`pull_request` targeting `oss-migration`, and `push` to `oss-migration`; switch to `main` +when the branch lands): + +**`nx-affected-checks`** (always runs): ``` 1. checkout (fetch-depth: 0) -2. setup-node@v4 (node-version: '24') + corepack enable +2. setup-node@v6 (node-version: '24') + corepack enable 3. pnpm install --frozen-lockfile -4. nrwl/nx-set-shas (main-branch-name: oss-migration) +4. nrwl/nx-set-shas@v5 (main-branch-name: oss-migration) 5. nx affected -t lint test typecheck build --parallel=3 ``` -**Type-drift guard** (same workflow, separate job, conditional on `supabase/**` or -`libs/core/src/types/**` changes): `supabase start` (lockfile-pinned CLI) → -`pnpm gen-types-local && pnpm gen-better-types` → -`git diff --exit-code libs/core/src/types/supabase-types.ts`. +**`supabase-type-filter` + `supabase-type-drift`** (type-drift guard — **commented out during +the oss-migration phase**; verified once on CI 2026-07-14; re-enable at landing on `main` or +Task 8 interlock): path filter on `supabase/**` or `libs/core/src/types/**` → +`pnpm supabase db start` (DB-only — not full `supabase start`) → `pnpm gen-types-local && +pnpm gen-better-types` → `git diff --exit-code libs/core/src/types/supabase-types.ts`. **`.github/CODEOWNERS`:** `supabase/** @bobbybol`. -Prove CI red/green honestly: one PR with a deliberate type error (must fail), one with a -schema change without regenerated types (drift guard must fail), then the clean pass. +Original plan called for three probe PRs with drift always on; migration-phase scope: main lane +green on push, drift guard verified once then disabled until post-migration (drift-fail probe +deferred to re-enable). -**Done when:** all three probe PRs behaved correctly on the `oss-migration` branch and the -workflow is merged into it. +**Done when:** workflow on `oss-migration`, `nx-affected-checks` green in CI, drift guard +implemented and smoke-tested, CODEOWNERS in place — met 2026-07-14. --- -## Task 8 — Interlock: adopt the 002b baseline +## Task 8 — Interlock sign-off (light) -- [ ] **Status:** Not started -- **Depends on:** Tasks 5, 7; **blocks on 002b Task 6** (verified baseline) +- [x] **Status:** Complete (2026-07-14) +- **Depends on:** Tasks 4, 5, 7 (002b complete) +- **Executor:** maintainer (clean-clone proof) -The join point of the two tracks (roadmap): +002b finished first; Tasks 4–5 already consumed the handoff (real +`supabase/migrations/20260710120000_init.sql`, types committed, local start verified). **No +placeholder to delete, no forced regen, no drift-guard re-enable here** — drift guard stays +commented out until post-migration (see Task 7 decisions log [7]). -1. Delete the placeholder migration from Task 4 if one was created (002b's baseline replaces it). -2. `supabase db reset` → `pnpm generate-types:local` → commit regenerated - `supabase-types.ts`. -3. Type-drift guard green in CI against the real baseline. -4. From a clean clone: `pnpm install` → `supabase start` → `pnpm generate-types:local` → - `nx run-many -t typecheck build` — the full ADR-004 bootstrap flow, all green. +**Single step — clean-clone golden path** (fresh directory; no existing Supabase Docker volume): -**Done when:** step 4 passes from a clean clone; roadmap interlock marked reached. +```bash +corepack enable +pnpm install --frozen-lockfile +pnpm supabase start +pnpm generate-types:local # expect git diff empty on libs/core/src/types/supabase-types.ts +pnpm exec nx run-many -t typecheck build -p api,worker,core +``` + +**Done when:** all commands succeed; result recorded in decisions log; parent plan interlock +marked reached — met 2026-07-14. --- ## Task 9 — DO deploy baseline -- [ ] **Status:** Not started -- **Depends on:** Task 2 (Task 8 preferably done first, not required) +- [x] **Status:** Complete (2026-07-14) +- **Depends on:** Task 2 (Task 8 sign-off optional but recommended before first deploy) - **Executor:** maintainer (DO account access) -Per ADR-006 decision 8 — branch-based DO App Platform deploy, no graph awareness, accepted: +Per ADR-006 decision 8 — branch-based DO App Platform deploy (buildpack path), no graph +awareness, accepted. Runbook: `docs/deployment/digital-ocean-buildpack.md`. | Component | Build command | Run command | |---|---|---| -| `api` | `pnpm install && nx build api` | `node dist/apps/api/main.js` | -| `worker` | `pnpm install && nx build worker` | `node dist/apps/worker/main.js` | - -- Source branch: `oss-migration` (a non-production DO app; repointed/recreated at landing). -- Config injection: inline `NXT_CONFIG_JSON` env var (ADR-007 decision 4; DO has no volume - mounts) — the everything-off default config is sufficient at this stage. -- No DB attachment needed yet (nothing uses it at hello-world stage). +| `api` | `corepack enable && pnpm install --frozen-lockfile && pnpm exec nx sync && pnpm exec nx build api` | `node apps/api/dist/main.js` | +| `worker` | `corepack enable && pnpm install --frozen-lockfile && pnpm exec nx sync && pnpm exec nx build worker` | `node apps/worker/dist/main.js` | -May be **parked** (like 002b Task 9.3) if the maintainer prefers to defer DO spend; the -golden path is then proven through Task 8 + Docker, with deploy following before the first -capability import completes. +- Source branch: `oss-migration` (non-production DO app; repointed/recreated at landing). +- App-wide env: `NODE_ENV=production`; `NX_DAEMON=false` at build time (avoids buildpack + export errors on `.nx/workspace-data/`). +- Config: bundled `config.default.json` sufficient; optional `NXT_CONFIG_JSON` later. +- No DB attachment at hello-world stage. -**Done when:** both components deploy and `api`'s `/health` answers publicly — or the task is -explicitly parked with a note and a resume condition. +**Done when:** both components deploy and `api`'s `/health` answers publicly — met 2026-07-14. --- ## Task 10 — Reintroduce git hooks (roadmap assumption 7) -- [ ] **Status:** Not started +- [x] **Status:** Complete (2026-07-14) - **Depends on:** Tasks 2, 5 (stable lint/typecheck targets); timing at maintainer discretion Fresh setup in the new workspace (do not port `legacy/.husky/`): husky + lint-staged as devDependencies, `prepare` script, pre-commit running lint-staged (ESLint on staged `.ts` -files only — fast by construction). Keep it lean; CI remains the authority. +files) then `nx affected -t typecheck --uncommitted`. **Amended from original ESLint-only +scope:** maintainer requested typecheck on commit (legacy had global `tsc`; new hook uses Nx +affected for scoped, graph-aware checks). CI remains the authority for the full affected lane. **Done when:** a commit with a lint error in a staged file is blocked locally; a clean commit -passes without noticeable delay. +passes without noticeable delay — met 2026-07-14. --- ## Task 11 — Close out -- [ ] **Status:** Not started +- [x] **Status:** Complete (2026-07-14) - **Depends on:** Tasks 1–8, 10 (9 may be parked) -- **`AGENTS.md` Commands section:** update to the real new-workspace commands (pnpm/nx - equivalents). This is a deliberate, minimal exception to the "docs deferred" rule - (roadmap assumption 6): agents executing the capability imports need working commands. - Everything else (root README etc.) stays deferred. -- Roadmap: 002c → Completed; capability imports (002d…) unblocked; record ADR-005's decision - point is now approaching (assumption 2: decide before the worker host gets its first real - capability). -- Decisions log below: complete, including exact versions chosen (Nx patch, pnpm, Node, - Supabase CLI). +- **`AGENTS.md` Commands section:** updated to pnpm/nx equivalents (lint bar, per-target, + affected/CI, pre-commit behaviour, serve, type-gen). Minimal exception to docs-deferred rule + (roadmap assumption 6); root README etc. still deferred. +- Roadmap: 002c → Completed; capability imports (002d…) unblocked; **ADR-005 decision point + now approaching** (assumption 2: decide before the worker host gets its first real capability). +- Decisions log below: complete (Nx **23.0.2**, pnpm **11.12.0**, Node **24**, Supabase CLI + **2.109.1**, husky **9.1.7**, lint-staged **17.0.8**). --- @@ -324,4 +403,204 @@ passes without noticeable delay. > Append here as the plan is executed. Format: `YYYY-MM-DD — [task] — note` -_(empty)_ +- 2026-07-14 — [1] — Nx **23.0.2**, pnpm **11.12.0**, Node **24** on all pinning surfaces. + Root package name `nxt-backend` (unscoped). `tsconfig.base.json` uses `customConditions: + ["source"]` for TS-solution exports. +- 2026-07-14 — [1] — `create-nx-workspace@23.0.2 --preset=ts --package-manager=pnpm`. Merged + `.gitignore` (lockfile tracked; depth-aware `dist/`/`node_modules/` ignores; `legacy/` + tracked). `.nxignore` excludes `legacy`. `pnpm-workspace.yaml` `allowBuilds` whitelists + native build scripts (`@swc/core`, `nx`, etc.) — do not use `pnpm approve-builds` (leaves + invalid placeholder text). +- 2026-07-14 — [2] — Nx 23 `@nx/nest:application` defaults changed: pass + `--linter=eslint --unitTestRunner=jest --e2eTestRunner=none` explicitly. Generated hosts use + `nx:run-commands` + `webpack-cli build` (inferred by `@nx/webpack/plugin`). +- 2026-07-14 — [2] — Restructured to legacy-aligned layout: `src/modules/` (removed `src/app/`, + `src/assets/`); `libs/core/src/modules/platform/`. Webpack `assets: []`. +- 2026-07-14 — [2] — ESLint: root `eslint.config.mjs` with team rules ported from + `legacy/eslint.config.ts`. Legacy launch configs removed from `.vscode/launch.json`. +- 2026-07-14 — [2] — `test` target set to `nx:noop` on api, worker, core until real tests + exist. Lint bar: `nx run-many -t lint typecheck build -p api,worker,core`. +- 2026-07-14 — [2] — Deps policy: root = tooling only (`@nx/*`, eslint, webpack, supabase CLI, + etc.); Nest runtime deps on each host's `package.json`. `@nxt/core` is a `workspace:*` + devDependency of `@nxt/api`. **Superseded 2026-07-14 (Task 3) — see the correction note + below: this should have been (and now is) a regular `dependencies` entry.** +- 2026-07-14 — [2] — Boundary probe: undeclared `@nxt/worker` import in api → `TS2307`; reverted. +- 2026-07-14 — [2] — Webpack externals fix for pnpm (see Task 2 note above). +- 2026-07-14 — [4-prep] — Supabase CLI `2.109.1` pinned at root; `pnpm supabase` script; + `docs/deployment/supabase.md` updated. Root `supabase/` chain present (002b); full Task 4 + closure (local start verified) still pending. +- 2026-07-14 — [3] — **`deployment.adminOrganizationId` / `systemWalletId` (register #22 / + ADR-007 Amendment) resolved:** both stay as plain configured Zod fields (optional, no DB + query at load or boot). Rejected the "drop from schema, query `organizations` at call sites" + and "boot-time DB query into `getConfig()`" alternatives — the maintainer wants the config + layer to stay simple and fully DB-free; the DB-native fact (`organizations.organization_type + = 'PLATFORM_OPERATOR'`, read directly by RLS per the 2026-07-13 GUC-removal amendment) and + the config value are accepted as two independently-set facts that should agree but aren't + mechanically linked at this stage. Both fields optional in the Zod shape so a zero-config + boot still validates; a capability that actually depends on one calls a co-located + `requireDeploymentField`-style helper (sibling to `requireEnv`, same `MISSING deployment. + — required by ` fail-fast convention) **at its own point of use, not at global + load** — deferred to whichever task first adds a real consumer (2002d), since no consumer + exists yet and the helper would have zero callers in Task 3. +- 2026-07-14 — [3] — **Drift mitigation (ADR-007 Deferred: "DB existence validation of + `adminOrganizationId`") scoped, not built in Task 3:** a one-time (memoized, not per-request), + **warn-only** boot/first-use check will compare `getConfig().deployment.adminOrganizationId` + against the DB's `PLATFORM_OPERATOR` `organizations` row and log on mismatch/absence; boot + is never blocked by this check (mirrors ADR-007's own "warn-level" framing — a hard fail here + would make a DB-side admin-org change brick boot until config is also edited and redeployed, + cutting against the Amendment's "flipping the flag is friendlier to operate" rationale). + Deferred until a Supabase client first exists in a wired app — Task 3 stays DB-free per its + own "done when" (zero-env boot). Not extended to `systemWalletId` (no analogous DB-side + marker exists for wallets; explicitly not pursued now — would require its own schema/ADR + work in a `wallets` domain that hasn't been imported yet). +- 2026-07-14 — [3] — **`NXT_CONFIG_URL` slot simplified:** no runtime env-var check/branch in + the loader (not even a throwing guard) — just a code comment at its position in the + precedence chain, pointing at ADR-007 decision 4. Rationale: it isn't documented anywhere as + usable (only ADR-007 mentions it, which itself says "not implemented"), so an operator + can't organically set it by accident; building a branch (+ test) for a source with zero + current consumers (no CMS, no Spaces bucket) was judged not worth it. `NXT_CONFIG_JSON` and + `NXT_CONFIG_PATH` are both fully implemented — each has a real near-term consumer (Task 9 DO + deploy; Task 6 Docker/k8s bind-mount and local dev respectively). +- 2026-07-14 — [3] — **Demo capability is temporary scaffolding, not a kept reference.** It + exists solely to prove the three Tier-1 honesty behaviors (flag off → not instantiated; flag + on → loads; flag on + required env missing → `MISSING …` boot block) and to give later + capabilities a copy-able `requireEnv` pattern. Named unambiguously (`demo` capability key, + `DemoModule`, `modules/demo/`) so it's grep-able; to be deleted once the first real Tier-1 + capability lands and demonstrates the same pattern in its place (flag this at that point — + do not let it linger as unintentional permanent code). +- 2026-07-14 — [3] — **Frontend/shared-resource config delivery stays deferred** per ADR-007 + decision 11 — no `public.branding`-style placeholder added ahead of a real consumer. Convention + for later: `public` carries browser-safe *values* only; actual assets (logos, etc.) live in + object storage and get referenced from `public` as a URL string once a consumer exists. +- 2026-07-14 — [3] — **Implementation note (discovered, not pre-planned): `AppModule` must be + imported dynamically in `main.ts`, after `loadConfig()`.** A static `import { AppModule } from + './modules/app.module'` is hoisted and evaluates the module's `@Module(...)` decorator (which + calls `demoModules(getConfig())`) before `bootstrap()`'s own body — including `loadConfig()` — + ever runs, so `getConfig()` would always throw "before load". Fix: `loadConfig()` first, then + `const { AppModule } = await import('./modules/app.module.js')` (note: the dynamic import + specifier needs the explicit `.js` extension for `tsc --build` under `moduleResolution: + nodenext`, even though the equivalent static import does not — TS resolves dynamic `import()` + expressions under stricter ESM rules regardless of the CJS/ESM mode inferred for the file). + This is the pattern every later app-level `main.ts` copies when adding a real capability tree. +- 2026-07-14 — [3] — Config layer implementation landed: `libs/core/src/config/{schema,loader, + index,require-env}.ts`; `zod` (`^4.4.3`) and `@nestjs/common` (`^11.0.0`, for the demo + module's decorators) added as real `@nxt/core` dependencies. Temporary `demo` capability in + `libs/core/src/modules/demo/` (schema + module + `demoModules()` contribution function). + `core`'s jest `test` target re-enabled (removed the `nx:noop` override); added a + `moduleNameMapper` stripping `.js` from relative import specifiers so Jest resolves the + NodeNext-style `.js`-suffixed imports back to their `.ts` siblings. 10 tests across + `loader.spec.ts`, `index.spec.ts`, `demo-modules.spec.ts` cover precedence order, + `$schemaVersion` rejection, freeze, `getConfig()`-before-load, and all three demo honesty + behaviors. Verified live against the built `dist/apps/{api,worker}/main.js` (not just unit + calls): zero-env boot, `NXT_CONFIG_JSON` override, and the demo's missing-env boot block, all + behaved as designed. `nx run-many -t lint typecheck build test -p api,worker,core` green from + a cold `nx reset`. +- 2026-07-14 — [3] — **Correction: `@nxt/core` moved from `devDependencies` to `dependencies` + in both `apps/api/package.json` and `apps/worker/package.json`** (supersedes the Task 2 note + above). Task 2's classification was made without checking whether webpack actually inlines or + externalizes the package; it does not inline it — `dist/apps/api/main.js` contains a real + `module.exports = require("@nxt/core")` (confirmed by inspecting the bundle directly), so the + running process needs `@nxt/core` genuinely resolvable at runtime, not just at build time. + `webpack-node-externals`'s `modulesFromFile` externalizes both `dependencies` and + `devDependencies` by default, so that part didn't matter — but the two Nx executors already + wired into each host's `prune`/`prune-lockfile`/`copy-workspace-modules` targets + (`@nx/js:prune-lockfile`, `@nx/js:copy-workspace-modules`) only read `packageJson.dependencies` + when deciding which workspace packages to rewrite into `workspace_modules` for a pruned + deploy artifact (confirmed by reading both executors' source); `devDependencies` are ignored. + Left as a devDependency, a genuinely pruned build (Task 6 Docker copying only + `dist/apps/api`, or `pnpm deploy --prod` per Task 6's plan text, which explicitly excludes + devDependencies) would ship a `main.js` requiring a package absent from the pruned output — + a latent `MODULE_NOT_FOUND` at container boot, masked so far because dev/test always run from + the full workspace checkout where `node_modules/@nxt/core` exists regardless. Verified the fix + by actually running `nx run api:prune` / `nx run worker:prune` after reclassifying: the pruned + `dist/apps/{api,worker}/package.json` now rewrites `@nxt/core` to + `"file:./workspace_modules/@nxt/core"` and the real package contents are copied into + `workspace_modules/@nxt/core`. Full `lint typecheck build test` bar re-verified green after + the change. Caught by the maintainer noticing the question was worth double-checking rather + than trusting the original Task 2 note (or this agent's own initial, incorrect restatement of + it) at face value. +- 2026-07-14 — [4] — **Local start verified** from repo root via `pnpm supabase start` (CLI + `2.109.1`, lockfile-pinned). Used existing 002b root chain as-is — no placeholder migration. + Baseline `20260710120000_init` is the sole applied migration; Postgres **17.6** on + `127.0.0.1:54322`, API `127.0.0.1:54321`. First run hit a transient ECR + `toomanyrequests` on `postgres:17.6.1.141`; CLI retried and succeeded. Informational gotrue + version warning (local `v2.192.0` vs linked remote `v2.193.0`) — no action taken. Log showed + `Starting database from backup...` (existing local Docker volume, not a fresh init). +- 2026-07-14 — [5] — Type-gen pipeline re-established at root. Scripts: + `gen-types-local` / `gen-better-types` / `generate-types:local` in root `package.json`. + `better-supabase-types@2.7.6` pinned (root devDependency). Json fix ported to + `supabase/scripts/fix-supabase-json-type.js` (co-located with `supabase/snippets/` pattern; + rejected root `tools/` folder). Intermediate `supabase/generated-types.ts` gitignored. + Generated output committed at `libs/core/src/types/supabase-types.ts` (3,576 lines; 002b + spot-check passes: `meter_command_batches` present, `directive_*`/`payouts`/`devices` absent, + `PLATFORM_OPERATOR` + `rls_check_if_admin_org_member` present). First run needed + `libs/core/src/types/` directory created (moot once file is committed). +- 2026-07-14 — [5] — **Type import surface:** `@nxt/core/types/supabase-types` package subpath + export in `libs/core/package.json` (not barrel re-export from root `@nxt/core` — keeps + hundreds of generated symbols off the main entry autocomplete; same ergonomics as legacy + `@core/types/supabase-types` without tsconfig path aliases). Golden-path probe: + `apps/api/.../health.service.ts` imports `OrganizationTypeEnum` via `satisfies` (no `/health` + contract change). `nx run core:build` + `nx run-many -t typecheck -p api,core` green. +- 2026-07-14 — [6] — **Docker runtime: Nx prune, not `pnpm deploy`.** ADR-006 decision 7 and + the original Task 6 text specified `pnpm deploy --prod --filter=@nxt/${APP}`. At execution + time, pnpm 11 rejects deploy without `inject-workspace-packages=true` (or `--legacy`). Rather + than opt into global injection (dev-DX concerns) or `--legacy` in a greenfield repo, runtime + pruning uses the Nx targets already wired in Task 2/3: `nx sync` → `nx run ${APP}:prune` + (build + `prune-lockfile` + `copy-workspace-modules`). Runtime stage copies + `apps/${APP}/dist/` and runs `pnpm install --prod --frozen-lockfile + --config.node-linker=hoisted` there — hoisted linker required so `workspace_modules/@nxt/core` + can resolve its own prod deps (e.g. `zod`) under Node ESM resolution. **Output path + correction:** webpack emits to `apps/${APP}/dist/` (not legacy `dist/apps/${APP}/`); ADR-006 + decision 8 / Task 9 DO run commands updated to match. Verified: `docker build + --build-arg APP=api|worker .`; both containers boot on default config; `api` `/health` answers + inside the container. +- 2026-07-14 — [7] — **CI on `oss-migration`.** `.github/workflows/ci.yml` + + `.github/CODEOWNERS` (`supabase/** @bobbybol`). Main job **`nx-affected-checks`**: checkout + (fetch-depth 0) → `setup-node@v6` + Corepack → `pnpm install --frozen-lockfile` → + `nrwl/nx-set-shas@v5` (`main-branch-name: oss-migration`) → `nx affected -t lint test + typecheck build --parallel=3`. Verified green on push to `oss-migration`. +- 2026-07-14 — [7] — **Type-drift guard deferred during migration.** Jobs + `supabase-type-filter` (path gate) + `supabase-type-drift` implemented in the same workflow + but **commented out** until post-migration. Rationale: full-stack + `supabase start` in CI is heavy and flaky (Docker image pulls); typecheck already catches + wrong/missing type *usage*; the guard mainly prevents schema changes without regen when no + consumer exists yet. **Smoke-tested once** with jobs enabled — all green on GHA. + Re-enable: uncomment both jobs in `ci.yml` when landing on `main` (update branch triggers + and `main-branch-name` too). Not gated on Task 8. +- 2026-07-14 — [7] — **Drift guard uses DB-only start.** `pnpm supabase db start` (not full + `supabase start`) is sufficient for `gen types --local` — verified locally and on CI; fewer + containers, faster, no API/gotrue/analytics dependency. +- 2026-07-14 — [7] — **GitHub Actions Node 24 majors.** Bumped to Node-24-native action + versions at scaffold time to avoid deprecation annotations: `actions/checkout@v6`, + `actions/setup-node@v6`, `dorny/paths-filter@v4`, `nrwl/nx-set-shas@v5` (commented drift + block uses the same pins). +- 2026-07-14 — [8-plan] — **Task 8 collapsed to light sign-off.** 002b finished before 002c + Tasks 4–5 consumed the handoff (real init migration, no placeholder, types committed). Original + interlock steps (delete placeholder, forced regen, drift guard live) dropped as redundant. + Remaining Task 8: maintainer clean-clone golden path only (`pnpm install` → `supabase start` + → `generate-types:local` → `nx run-many -t typecheck build`). Drift guard re-enable stays + separate (post-migration / Task 7). +- 2026-07-14 — [8] — **Interlock sign-off complete.** Maintainer ran clean-clone golden path: + `corepack enable` → `pnpm install --frozen-lockfile` → `pnpm supabase start` → + `pnpm generate-types:local` (zero diff on `libs/core/src/types/supabase-types.ts`) → + `pnpm exec nx run-many -t typecheck build -p api,worker,core` — all green. Roadmap interlock + (002b ↔ 002c) marked reached; 002d capability imports unblocked. +- 2026-07-14 — [9] — **DO App Platform buildpack baseline.** One app on branch `oss-migration`, + two components (`api` Web Service + `/health` check, `worker` Worker). Buildpack path (not + root Dockerfile). App-wide env: `NODE_ENV=production`, `NX_DAEMON=false` (build-time — avoids + buildpack export failure on `.nx/workspace-data/…/daemon.log`). Zero `NXT_CONFIG_JSON`; bundled + default config boots both hosts. Public `/health` and worker heartbeat verified. Runbook: + `docs/deployment/digital-ocean-buildpack.md`. +- 2026-07-14 — [10] — **Git hooks reintroduced (roadmap assumption 7).** husky **9.1.7** + + lint-staged **17.0.8** at root; `prepare`: `"husky"`; `.husky/pre-commit`: `lint-staged` then + `pnpm exec nx affected -t typecheck --uncommitted`. lint-staged: staged `**/*.ts` → + `eslint --max-warnings=0 --no-warn-ignored` (block-only, no `--fix`; `legacy/**` ignored via + ESLint flat config). **Amended from Task 10 ESLint-only draft:** maintainer requested + typecheck on commit; chose `nx affected --uncommitted` over legacy global `tsc` or + `run-many -p api,worker,core` for graph-aware scope aligned with CI. Not ported: + `legacy/.husky/` boilerplate or `tsc -p tsconfig.base.json`. +- 2026-07-14 — [11] — **002c closed out.** `AGENTS.md` Commands → pnpm/nx lint bar and related + targets; roadmap marks 002c Completed; 002d capability imports next. ADR-005 inter-host + decision point flagged approaching (before worker's first real capability). Housekeeping + (same session, not separate tasks): `.pnpm-store/` removed from git + gitignored; + `.gitignore` / `.cursorignore` tidied. diff --git a/docs/plans/002-oss-migration/002d-platform-core-import.md b/docs/plans/002-oss-migration/002d-platform-core-import.md new file mode 100644 index 0000000..af5fdb7 --- /dev/null +++ b/docs/plans/002-oss-migration/002d-platform-core-import.md @@ -0,0 +1,645 @@ +# 002d — Platform Core Import (Foundation) + +**Parent plan:** `docs/plans/002-oss-migration.md` (read it first) +**Decisions:** ADR-004 (capability map / §5 — amended by this plan), ADR-007 (config & wiring — +amended by this plan), ADR-008 (Phase 3 incremental import), **ADR-013** (capability-owned +behavior over shared core entities — *authored by this plan*, Task 1) +**Created:** 2026-07-15 +**Status:** **Completed** (2026-07-17) — Tasks 1–11 done; next 002e (ADR-005 Accepted 2026-07-17) +**Depends on:** 002b (database baseline) and 002c (scaffold, pipeline & config skeleton) complete; +interlock reached 2026-07-14. +**Execution model:** collaborative — division of labor is decided **per task/subtask as we go** +(see "Division of labor" in the parent plan). Ask which mode applies before starting a task. +Per-task `Executor:` notes mark where maintainer credentials/ownership are *required*; everywhere +else the mode is chosen per task. Respect the review cadence — address tasks one at a time, +stopping for review; never run ahead. + +--- + +## Purpose + +Import the always-on **platform core** — referred to hereafter as **Foundation** — into the new +workspace as the first real domain code (ADR-008 Phase 3, "platform core before any capability"). +Foundation is the identity/access spine every capability depends on: authentication, API keys, +accounts, members, organizations, user administration, and grids, over the cross-cutting infra +(Supabase client, HTTP, logging). + +This import is also the point at which several standing debts are paid down deliberately (rule 2): +the operations-database **dual-ORM is retired** (TypeORM → Supabase client), **real structured +logging** replaces `console.*`, the **admin-organization coupling** is removed from the config +surface, and the first **de-Nigeria / de-brand** cleanups land. It establishes the wiring, +testing, and placement patterns that every later capability import copies. + +## What "Foundation" is (and is not) + +**Foundation is not a library or a single object.** It is the **per-app selection** of always-on +modules. `libs/core` (`@nxt/core`) remains the shared kernel (config, types, cross-cutting infra); +shared domain services live in `libs/core`, app-only pieces (controllers, `auth`, `user-admin`) +live in their app; auxiliary utilities live in `libs/helpers`. Each host composes its own +Foundation explicitly (see Task 4). `api`'s Foundation is large; `worker`'s is tiny. + +## Decisions baked into this plan (do not relitigate — see the chat/decisions log) + +These were resolved during authoring. Execute to them; if reality contradicts one, stop and raise +it, don't silently diverge. + +1. **Scope (see table below).** Foundation = infra + `auth`, `api-keys`, `accounts`, + `members` (type-only), org **table** (Nest module skipped — Task 9), `user-admin` (whole), + grids **table** (Nest module skipped — Task 10). Deferred / re-homed: Nest `grids` CRUD/read, + metering-coupled grid methods, `download`, `routers`, notification-core, `websocket`, + `agents`, `dcus`, `poles`. These are **amendments to ADR-004 §5** (Task 1; Task 10 refined + Nest `grids` from “partial import” to “table only”). +2. **Operations-DB TypeORM is dropped entirely.** No ops-DB `*.entity.ts`, no `CoreTypeOrmModule` + in Foundation. Services are (re-)expressed against the Supabase client using generated + `supabase-types`. Timeseries TypeORM is untouched (decided at Monitoring, 002e). +3. **Naming:** `@nxt/core` = kernel; the always-on domain layer = **Foundation**, selected per app. + Legacy `Core*` class prefixes are dropped on import. +4. **Wiring is centralized-explicit per app** (ADR-007 §7 amendment): plain named + `infrastructure` / `foundation` arrays + inline Tier-1 conditionals in each `app.module.ts`; + **no `xModules(config)` contribution wrappers**; per-capability fail-fast lives in the + module/adapter wiring (ADR-007 §9), not a wrapper. Inline in `app.module.ts` now, with an + extract trigger to a co-located `app.composition.ts` (~20+ imports, or when Tier-2 `forRoot` + wiring starts crowding the Tier-1 booleans). Each app lists its **own** infra array. +5. **Single-pass import.** Foundation is always-on: no capability flags, and **no speculative + ports** (no auth-provider port — Supabase Auth is the only provider; ADR-001 trigger governs a + future SPI). +6. **Admin-org / config data-references:** the entire `deployment` config group is **dropped** + (`adminOrganizationId` + `systemWalletId`); auth does **not** compute an admin-org flag (it has + no in-scope reader — the only reader, `epicollect`, is deferred). Each value returns + DB-resolved with its owning capability (Field Ops / Payments). `is_nxt_grid_member` → + `is_admin_org_member` is the name to use **when reintroduced**. `NxtSupabaseUser` → + **`AuthenticatedUser`**. `systemWalletId` → **`bankingSystemWalletId`** when it returns (under + `capabilities.payments`, not a resurrected top-level group). ADR-007 amendment (Task 1). +7. **Boot model:** a host that wires Foundation/Supabase infra **requires DB connectivity and + fails fast on missing `SUPABASE_*`**. "Evaluation mode" means *capabilities off + local + Supabase*, not DB-less. **`worker` includes Supabase infra** in its Foundation. ADR-007 + amendment (Task 1). +8. **Supabase infra:** `SupabaseService` builds the admin client in a **provider** with + `requireEnv` (fail-fast at boot); **no `export const supabase` singleton**. Per-host env + validated at each provider's wiring — admin client (`SUPABASE_URL`, + `SUPABASE_SECRET_KEY`) both hosts; `SUPABASE_PUBLISHABLE_KEY` + JWT verification + (`SUPABASE_JWKS_URL` preferred; `SUPABASE_JWT_SECRET` legacy fallback) in auth + (`api` only). The query-type shortcut returns later via a **type-only probe** (no runtime + client). Cloudflare-5xx handling and `SUPABASE_QUERY_LIMIT` kept as-is. +9. **Logging:** Nest built-in `Logger` for now; **`GlobalLoggerModule` kept as a no-op slot** + in host `infrastructure` for a later `nestjs-pino` restore (option builders in + `logger.options.ts`). Structured JSON / pretty / Loki-Sentry remain Tier-3. No HTTP + auto-logging when pino returns (`autoLogging: false`). `console.*` fine for local + debug; replace within imported Foundation modules when structured logging returns. +10. **Types:** the **adjusted layer is reintroduced from the get-go** — + `libs/core/src/types/supabase-types-adjusted.ts` augments the generated `Database` with the + PostGIS `location_geom` types for `grids` **and** `poles`, exposed at its own subpath + (`@nxt/core/types/supabase-types-adjusted`). Convention: **`Database` is always imported from + the adjusted subpath**; only enum/row types come from the generated subpath (optionally + enforced with a restricted-import lint rule). +11. **Placement principle (ADR-013):** core entities are shared **data**; capability-specific + **behavior** lives in the owning capability, never bolted onto the core module (no + conditional-unlock methods, no plugin registry in the baseline). Task 10 applied this by + **not** porting Nest `grids` (call-site queries; metering methods → Metering) — refined from + the earlier “partial Nest import” framing. The "no-cracks" governance below remains mandatory. +12. **i18n / de-brand:** `grids.timezone` default `'Africa/Lagos'` → **`'UTC'`** via **amending + the 002b init migration** (Task 2). A running **internationalization & de-brand register** is + created (Task 1) and grows per import. Foundation code-level de-brand is done inline + (delete dead test code, fix comments, `AuthenticatedUser` rename). `organizations.timezone` + as a future column is *noted*, not acted on. +13. **Verification:** typecheck + a growing **seed** + a single **keep-or-dismiss test spike**; + **manual maintainer sign-off** is the standing fallback. Request-handling reads shift to the + **per-request user client** (admin client only pre-auth / privileged); RLS parity is part of + the bar. (Superseded: `accounts` is type-only — see Task 6 / decisions log.) + +## Scope + +| In Foundation (002d) | Deferred / re-homed | Where it goes | +|---|---|---| +| Infra: Supabase client, HTTP, **new pino logging** (config/types already in `@nxt/core`) | `download` | later (ADR-004 §5 amend) | +| `auth` (supabase + api-key strategies + guard) | `routers` | Production Monitoring (ADR-004 §5 amend) | +| `api-keys` lookup lives in auth (Task 7); `accounts` + `members` (type-only) | notification-core | first capability that writes notifications | +| `user-admin` (whole); org **table** (no Nest module) | `websocket` | Metering (first realtime emitter) | +| grids **table** + RLS + seed (no Nest module — Task 10) | `agents` | Metering/Payments (entity rides identity graph) | +| | Nest `grids` CRUD/read; metering-coupled grid methods | call-site queries / Metering (ADR-013) | +| | `dcus`, `poles` | Metering | + +## Non-goals (do not build here) + +- Any capability module (production, metering, payments, notifications, field-ops, automation). +- An auth-provider port / pluggable-auth SPI (ADR-001 trigger governs). +- ADR-005 inter-host communication — was out of scope here (002e prerequisite); **Accepted + 2026-07-17** on the parent roadmap. +- Loki/Sentry transports (Tier-3, deferred — only the config-assembled slots are left open). +- Re-enabling the CI type-drift guard (stays deferred per 002c Task 7). +- A full integration-test harness on spec (only the single scoped spike — Task 8). +- `organizations.timezone` column, `bankingSystemWalletId`, admin-org DB resolution (return with + their consumers). + +## Current state snapshot (2026-07-15 — start of 002d) + +- Workspace: Nx 23.0.2, pnpm, Node 24. Projects: `apps/api` (`@nxt/api`), `apps/worker` + (`@nxt/worker`), `libs/core` (`@nxt/core`). `legacy/` excluded from the graph. +- `@nxt/core` exports: `getConfig/loadConfig/setConfig`, `requireEnv`, `getPackageInfo`, + `demoModules`, `NxtConfig`; config under `src/config/`; generated types at + `src/types/supabase-types.ts` (subpath `@nxt/core/types/supabase-types`); temporary `demo` + capability under `src/modules/demo/`. +- Config schema (`src/config/schema.ts`): `$schemaVersion`, `deployment {adminOrganizationId?, + systemWalletId?}`, `public {platformName}`, `capabilities {demo?}`, `integrations {}`. **This + plan removes `deployment` and `demo`.** +- Hosts: `api` serves `GET /health`; `worker` logs a heartbeat. Both `main.ts` `loadConfig()` then + dynamic-import `AppModule`; both compose `[ ...alwaysOn, ...demoModules(getConfig()) ]`. +- Legacy Foundation source (reference, in `legacy/`): `apps/tiamat/src/modules/{auth, api-keys, + accounts, agents, organizations, user-admin, grids, ...}` (controllers/services) + + `libs/core/src/modules/{accounts, api-keys, members, organizations, grids, ...}` + (TypeORM entities + some services) + `libs/core/src/modules/supabase.module.ts`, + `logger-module.ts`, `global-http-module.ts` + `libs/helpers/`. + +## Import ledger + +Authoritative record of legacy Foundation source files and their disposition. **A legacy file is +deleted only when fully superseded.** For entangled files, record the destination **per behavior** +(ADR-013). Keep this current as tasks execute. + +| Legacy source | Disposition | Notes | +|---|---|---| +| `libs/core/src/modules/supabase.module.ts` | **deleted** (Task 11) | Was imported Task 4 → `libs/core/src/modules/supabase/` | +| `libs/core/src/modules/logger-module.ts` (+ LokiService) | **deleted** (Task 11) | Superseded by `GlobalLoggerModule` no-op stub; Loki not ported | +| `libs/core/src/modules/global-http-module.ts` | **deleted** (Task 11) | Was imported Task 4 | +| `libs/core/src/modules/accounts/**` | **deleted** (Task 11) | Type-only disposition (Task 6); tiamat `accounts` host module also deleted | +| `libs/core/src/modules/api-keys/**` | **deleted** (Task 11) | Absorbed into auth (Task 7); tiamat `api-keys` host module also deleted | +| `libs/core/src/modules/members/**` | **deleted** (Task 11) | Type-only (Task 6) | +| `apps/tiamat/src/modules/api-keys/**` | **deleted** (Task 11) | Absorbed into auth (Task 7) | +| `libs/core/src/modules/organizations/**` + `apps/tiamat/.../organizations/**` | **deleted** (Task 11) | Nest skipped Task 9; org table remains Foundation data | +| `libs/core/src/modules/customers/dto/create-customer.dto.ts` | **deleted** (Task 11) | Imported Task 9 → `@nxt/core`; rest of legacy customers module retained | +| `apps/tiamat/src/modules/auth/**` | **deleted** (Task 11) | Imported Task 7 → `apps/api/.../auth/` | +| `apps/tiamat/src/modules/user-admin/**` | **deleted** (Task 11) | Imported Task 9 → `apps/api/.../user-admin/` | +| `apps/tiamat/src/modules/grids/**` + `libs/core/.../grids/**` | **Nest skipped** (Task 10); metering **pending**; `GET /:id` **removed** (Task 11) | Annotated services retained. Controller kept minus Flow XO `GET /grids/:id`. Entity + service stay until Metering absorbs connectivity/cabin-credit | +| `libs/core/src/types/supabase-types-adjusted.ts` | **deleted** (Task 11) | Reintroduced Task 3 → `@nxt/core/types/supabase-types-adjusted` | +| `libs/helpers/src/*.ts` | per-use, file-granular | `git mv` each file at first import (with its `.spec`) | + +--- + +## Task 1 — Decision records & registers (upfront) + +- [x] **Status:** Done (2026-07-15) +- **Depends on:** nothing (guides the rest — do first) + +Record the decisions before the code so the import executes against them. + +1. **Author ADR-013** — "Capability-owned behavior over shared core entities" (data in core, + behavior in the owning capability; no conditional-unlock methods; no plugin registry in the + baseline; grids worked example; metering/metering-monitoring seam as the illustrative case). + Confirm the next free ADR number at write time. +2. **AGENTS.md** — add the ADR-013 row to the ADR index table; add a brief read-if-relevant + pointer in the Backend/architecture section (**not** an always-applied rule — avoid + context-bloat). Optionally a `.cursor/rules/*.mdc` with `alwaysApply: false` + a description. +3. **Amend ADR-004 §5** — download/routers out of platform core; notification-core & websocket + deferred; agents/dcus/poles re-homed; grids partial. Keep the capability map aligned. +4. **Amend ADR-007** — (a) wiring is centralized-explicit per app, superseding decision 7's + contribution-function mechanism; (b) the `deployment` group is dropped and admin-org is + resolved DB-side per consumer (closes the "backend consumers" open item); (c) the refined boot + model (foundation-wired hosts require DB / fail-fast on `SUPABASE_*`; "eval mode" clarified). +5. **Create the internationalization & de-brand register** — + `docs/plans/002-oss-migration/internationalization-and-debrand-register.md` (migration-wide, + grows per import). Seed it with: `grids.timezone` default (Task 2), `AuthenticatedUser` rename, + and the `organizations.timezone` future-candidate note. +6. **Roadmap** (`docs/plans/002-oss-migration.md`) — sub-plan index 002d → In progress; standing + assumptions: resolve the "module split philosophy" open item by pointing at ADR-013; record + ADR-005 as an explicit **002e authoring prerequisite**; note the "no-cracks" governance rules. + +**Done when:** ADR-013 exists and is indexed; ADR-004 §5 and ADR-007 amendments are in; +the i18n register exists; the roadmap reflects the above. + +**Done (2026-07-15):** ADR-013 authored + AGENTS.md ADR-index row; ADR-004 §5 amended (Foundation +narrowed); ADR-007 amended (2026-07-15: `deployment` group dropped, explicit central per-host +wiring, DB-required boot model); `internationalization-and-debrand-register.md` created + seeded; +roadmap updated (002d index → In progress, no-cracks governance, assumption 12 module-split→ADR-013, +assumption 2 ADR-005→002e prerequisite, assumption 9 deployment dropped, Foundation single-pass +note, deviation-recording + related-docs). The optional `alwaysApply:false` cursor rule was **not** +added (ADR-index row deemed sufficient, per "read-if-relevant, no bloat"). + +--- + +## Task 2 — Schema: `grids.timezone` default → `UTC` + +- [x] **Status:** Done (2026-07-16) +- **Depends on:** Task 1 +- **Executor:** maintainer (`supabase/**` under CODEOWNERS) + +**Amend the 002b init migration** so `grids.timezone` defaults to `'UTC'` (not `'Africa/Lagos'`). +Regenerate types (`pnpm generate-types:local`) and confirm no unexpected drift. Add a +**schema deviation register** entry (companion to 002b): object `grids.timezone`, change +*parameterize/neutralize default*, rationale *de-Nigeria the OSS baseline*, cutover implication +*default affects new inserts only — existing company grids retain stored values; company keeps its +own default or aligns*. Cross-link the i18n register. + +**Done when:** init migration amended; types regenerated clean; deviation + i18n registers updated. + +--- + +## Task 3 — Reintroduce the adjusted types layer + +- [x] **Status:** Done (2026-07-16) +- **Depends on:** Task 2 + +1. Add `libs/core/src/types/supabase-types-adjusted.ts` augmenting the generated `Database` with + `location_geom` (`{ type: 'Point'; coordinates: number[] }`) for **`grids` and `poles`** (as + legacy). +2. Add the package subpath export `@nxt/core/types/supabase-types-adjusted` in + `libs/core/package.json`. +3. Convention: **`Database` imported only from the adjusted subpath**; enum/row types from the + generated subpath. Optionally add an ESLint `no-restricted-imports` rule enforcing it. + +**Done when:** the adjusted `Database` is importable at its subpath; `nx run-many -t typecheck` +green; the import convention is documented (and lint-guarded if adopted). + +--- + +## Task 4 — Infra + explicit composition (Supabase provider, logging slot, demo removal) + +- [x] **Status:** Done (2026-07-16) +- **Depends on:** Task 3 + +The foundational infra task (rule 1). Both hosts boot on real infra. + +1. **Supabase infra** in `@nxt/core`: `SupabaseService` builds the admin client in its constructor + via `requireEnv('SUPABASE_URL')` + `requireEnv('SUPABASE_SECRET_KEY')`; **remove the + `export const supabase` singleton**. Port `handleResponse` / `throwSupabaseError` / + Cloudflare-5xx handling as-is but route logging through Nest `Logger` (pino deferred). Keep + `SUPABASE_QUERY_LIMIT` a plain constant. (The query-type-shortcut `lib/supabase.ts` files are in + deferred capabilities; the **type-only probe** pattern is documented for their return — nothing + to build here.) +2. **HTTP infra:** port `global-http-module.ts` (minimal); wired in **both** hosts. +3. **Logging** in `@nxt/core`: Nest built-in `Logger` for now; keep `GlobalLoggerModule` as a + no-op slot in host `infrastructure` (+ `logger.options.ts` for nestjs-pino restore). + Structured JSON / pretty / Loki–Sentry remain Tier-3. Retire legacy + `logger-module.ts`/`LokiService` (not ported; delete at Task 11). +4. **Explicit composition:** replace `demoModules()` in both `app.module.ts` with plain named + `infrastructure` / `foundation` arrays + inline Tier-1 conditionals (empty capability set for + now). `api` infra = Logger + Supabase + HTTP; `worker` infra = Logger + Supabase + HTTP (+ + schedule/heartbeat). **Remove the `demo` capability** (`modules/demo/`, `demoModules` export, + `demo` from `capabilities` schema) and the `deployment` group from `config/schema.ts` + + `config.example.json` / `config.default.json`. +5. Update `.env.example` (root + per-host) with Supabase + logging documentation. + +**Done when:** both hosts boot on default config against a local Supabase; missing `SUPABASE_*` +fails fast with a clear `MISSING …`; logs use Nest `Logger` (`GlobalLoggerModule` stub ready for +pino); `demo`/`deployment` +are gone; `nx run-many -t lint typecheck build test -p api,worker,core` green. + +**Done (2026-07-16):** Supabase provider + HTTP + explicit `infrastructure`/`foundation` composition +on both hosts; demo + `deployment` removed; env layout + renamed Supabase vars; nestjs-pino tried +then deferred (webpack transport workers / console DX) with `GlobalLoggerModule` stub retained; +scaffold golden-path proofs removed (`getPackageInfo`, health type probes); `/health` keeps a +lightweight Supabase probe. Lint bar green. Deployment-docs polish left for Task 5 alongside seed. + +--- + +## Task 5 — Seed harness + +- [x] **Status:** Done (2026-07-16) — awaiting sign-off +- **Depends on:** Task 3 (schema/types stable) + +Establish `supabase/seed.sql` (or a seed script) that grows per import. 002d fixtures: an +organization (including the `PLATFORM_OPERATOR` row), accounts, members, an `api_keys` row, and a +grid — plus a **test auth user with `app_metadata` claims** (account_id, member_type, +organization_id) for auth/e2e. Doubles as the local-dev bootstrap. + +**Done when:** a fresh `pnpm exec supabase db reset` yields a coherent Foundation dataset and a +usable test user; documented in `docs/deployment/supabase.md` (or the local-dev doc). + +**Done:** +- `supabase/seed.sql` wired via `config.toml` `[db.seed]`; loop is `pnpm exec supabase db reset` + (local only — not for remote/prod). +- Fixtures: PLATFORM_OPERATOR + SOLAR_DEVELOPER orgs/wallets; two claimed auth users + (`superadmin@nxt-platform.com` / `SUPERADMIN`, `admin@nxt-solar.com` / `DEVELOPER`); + `api_keys` `dev-api-key-platform-superadmin`; grid **Demo Solar Grid** on org 2. +- Invite-path mirrored: auth insert → `handle_new_user` → `app_metadata` update → + `handle_update_user` → `members` insert. +- `docs/deployment/supabase.md` §4–5 updated (local seed + env pointer; dashboard bootstrap + replaced for local). + +--- + +## Task 6 — Disposition only (`accounts` / `members` / `api-keys`) + +- [x] **Status:** Done (2026-07-16) — docs/ledger only; awaiting sign-off +- **Depends on:** Task 4 (+ Task 5 context) + +No Nest modules land in this task. Decisions: + +- **`accounts` + `members`:** type-only (generated / inferred row types; no services). +- **`api-keys`:** absorbed into **Task 7** `ApiKeyStrategy` — inject `SupabaseService` admin + client and select at the call site (`is_locked` already gone). No `@nxt/core` api-keys module. +- Legacy TypeORM entities/services stay under `legacy/` until **Task 11** deletes them per ledger + (fully superseded once auth ships without them). + +**Done when:** ledger + Task 7/8 wording reflect the above; no code changes required in Task 6. + +--- + +## Task 7 — `auth` + +- [x] **Status:** Done & signed off (2026-07-17) +- **Depends on:** Task 6 + +Move the two Passport strategies + `AuthenticationGuard`. Rename `NxtSupabaseUser` → +**`AuthenticatedUser`**; **drop the admin-org membership flag** (no in-scope reader); **drop the +embedded `account` object** (TEMPORARY legacy); keep exposing raw `organization_id` + +`account_id` / claims. Bearer path does not call an accounts service. **API-key path:** inject +`SupabaseService` (admin) in `ApiKeyStrategy` and select the key + account graph inline (no +`ApiKeysService`). `console.*` → Nest `Logger` (pino restore later); de-brand comments. Validate +`SUPABASE_PUBLISHABLE_KEY` + JWT verification (`SUPABASE_JWKS_URL` preferred; +`SUPABASE_JWT_SECRET` legacy fallback) via `requireEnv` at auth wiring (`api` only). Restore +`enableCors()` + a global `ValidationPipe` on the `api` bootstrap. Add `@nestjs/passport`, +`passport`, strategy packages, and `jose` (JWKS) to `api` — not `@nestjs/jwt` / +`jsonwebtoken`. + +**Landed:** `apps/api/src/modules/auth/*` (strategies, guard, module, `/auth/me`); httpYac under +`apps/api/http/`; ADR-014; bootstrap CORS + `ValidationPipe`; ledger + i18n register updated. +**Verified:** seeded bearer JWT + `X-API-KEY` against local Supabase (maintainer, httpYac / +`/auth/me`). + +**Done when:** `api` authenticates a seeded Supabase JWT (bearer) and an `X-API-KEY`; the guard +attaches a populated `AuthenticatedUser`; `worker` requires none of the auth secrets. + +--- + +## Task 8 — Scoped test spike (keep-or-dismiss) + +- [x] **Status:** Done — **adopted** & signed off (2026-07-17) +- **Depends on:** Tasks 5, 7 +- **Executor:** collaborative; **dismiss → maintainer manual sign-off** + +A single, self-contained effort — do not muddle across other tasks. Stand up **one** integration +test (API-key admin select / strategy lookup against local Supabase + seed) and **one** e2e auth +test (the `X-API-KEY` guard path). Evaluate: adopt as the pattern others copy, **or** dismiss +cleanly and fall back to maintainer manual sign-off for the remaining modules. If the agent is +burning excessive tokens or getting stuck, dismiss and hand to manual sign-off. + +**Adopted pattern (copy this):** + +- Tests under `apps/api/test/` only (not co-located with `src/`): `unit/`, `integration/`, `e2e/`, + `helpers/`. Same for libs: `libs/core/test/unit/` only (libs stay unit-only; no integration/e2e + in the shared kernel — those belong on host apps). +- **Default** `nx test api` = **unit only** (stack-free; part of the lint bar). +- Opt-in: `nx run api:test-integration` / `nx run api:test-e2e` (local Supabase + seed; skip if + `SUPABASE_*` missing). See `docs/deployment/supabase.md` § automated tests. +- Integration: Nest testing module + real admin client against seed (e.g. `ApiKeyStrategy`). +- E2E today: **thin** HTTP slice for the path under test (real guard/controller/API-key strategy; + stub bearer). **Not** full `AppModule` — Jest CJS cannot load ESM-only `jose` via + `SupabaseStrategy`. Do **not** proliferate thin modules; full-app e2e is a follow-up when an + ESM/Jest harness exists. Bearer JWT remains httpYac until then. +- Remaining Foundation modules: use this pattern where it fits; otherwise maintainer manual + sign-off (httpYac). + +**Done when:** the spike runs green (adopted) *or* is explicitly dismissed with the rationale +recorded and manual sign-off adopted as the standing bar. + +--- + +## Task 9 — `organizations` + `user-admin` + +- [x] **Status:** Done — signed off (2026-07-17) +- **Depends on:** Task 7 + +Import `user-admin` **whole** (members + agents + customers — the recorded user-admin-specific +exception). Delete the dead commented test-user code; de-brand comments. Bring DTOs +(`CreateCustomerDto` in `@nxt/core` for worker reuse; other DTOs on the api module). + +**Nest `organizations` skipped:** legacy service had a single `findOne` and the only controller +route was commented out; nothing else injected the service. Org table stays Foundation data +(seed, RLS, health probe). Legacy Nest org files → delete at Task 11. + +**Client policy (Task 9):** `user-admin` uses the **admin client for the whole method** (Auth Admin +APIs + privileged follow-on writes). Do not mix user-client reads mid-flow here. API-key callers +still have no RLS-bound user client — **near-future:** ADR-014 §5.3 (attach `authenticated` client +after key validation) before growing more machine-callable data paths. User-client-by-default +returns with the **first real bearer read** that needs RLS (deferred — Task 10 Nest `grids` +skipped; no longer a grids HTTP smoke). + +**Landed:** `apps/api/.../user-admin/`; `CreateCustomerDto` in core; httpYac `user-admin.http`; +`handleSingle` + Cloudflare→503; ledger + i18n #4. Maintainer local serve + happy with smoke. + +**Done when:** endpoints compile and behave against the seed (maintainer httpYac / manual +sign-off; Task 8 pattern optional); ledger + i18n register updated. + +--- + +## Task 10 — `grids` (Nest skipped; table stays Foundation) + +- [x] **Status:** Done & signed off (2026-07-17) +- **Depends on:** Task 7 + +**Disposition (no Nest import):** audit showed no strong Foundation Nest surface. Shared +`findOne` is a TypeORM-era habit; with Supabase, callers should select at the call site. +`GET /grids/:id` is Flow XO-only (company automation) — **not ported**; **delete at Task 11**. +`PUT /grids` / `updateMany` existed so loch weather could write through api under the old +“only Tiamat writes ops DB” rule — that rule is **dropped** (modules stay self-contained and +may write ops DB directly). Create never existed as an API. Org-scoped list helpers / +`findAll` had **no in-repo callers**. + +**Do not** import metering-coupled methods (connectivity stats, cabin-credit / DCU–meter +orchestration) — per ADR-013 re-home to Metering when that capability lands. Retain +`legacy/.../grids/grids.service.ts` until that half is absorbed; Nest CRUD/read pieces +delete at Task 11 per ledger (entity stays with the retained metering half). + +Grid **table** + RLS + seed (id `1` on org 2) + adjusted geom types remain Foundation data — +same posture as Nest `organizations` skipped (Task 9). + +**Done when:** ledger + this task record the skip; decisions log notes the write-funnel drop and +cleanup of `findOne` / `GET /:id`; no Nest `grids` module in `@nxt/core` / `api`; no +`dcus`/`meters` pulled into 002d. + +--- + +## Task 11 — Close-out + +- [x] **Status:** Done & signed off (2026-07-17) +- **Depends on:** Tasks 1–10 + +- Delete superseded legacy Foundation files per the import ledger (whole-file, when fully + superseded). For grids (**annotate + remove `GET /:id`**): leave `grids.service.ts` + entity + intact with a ledger/annotation note (Metering pending; Nest CRUD not ported); **keep** + `grids.controller.ts` but drop the Flow XO `GET /grids/:id` route (`PUT` / connectivity / + download remain until their owners absorb them). +- Finalize the import ledger, schema deviation register, and i18n register. +- Verify the lint bar green across `api`, `worker`, `core`, and any imported `helpers` files. +- Roadmap: 002d → Completed; 002e next (ADR-005 was still the authoring prerequisite at close-out; + Accepted later the same day). +- Confirm `demo` and the `deployment` config group are fully gone. + +**Done when:** the done/exit bar (below) is met and recorded. + +**Landed (close-out):** Legacy Foundation deletes per ledger; grids annotated + `GET /:id` +removed; `demo`/`deployment` confirmed gone; lint bar green +(`nx run-many -t lint typecheck build test -p api,worker,core`); roadmap 002d → Completed; +ADR-005 was still open at 002d close-out; **Accepted 2026-07-17** before 002e authoring. + +--- + +## Done / exit bar for 002d + +- `api` boots via explicit central composition; `worker` boots with logging + Supabase infra; both + fail-fast on missing `SUPABASE_*`; `demo`/`demoModules` removed. +- Foundation imported on the Supabase client (no ops TypeORM; `CoreTypeOrmModule` gone from scope): + `auth` (`AuthenticatedUser`, admin-org flag dropped; no embedded `account`; API-key select + inline in strategy), `accounts` + `members` (type-only), org **table** (no Nest module), + `user-admin` (whole; admin-client privileged surface), grids **table** (no Nest module — + Task 10). Structured logging: Nest `Logger` + `GlobalLoggerModule` **no-op stub** (nestjs-pino + deferred Task 4); user-client-by-default on request-handler reads when those land (except + whole-method admin privileged surfaces like `user-admin`; first bearer RLS smoke deferred past + Nest-grids skip). +- Adjusted types layer live (grids+poles geom) with uniform `Database` import; `deployment` group + dropped; `grids.timezone` default `UTC` (init migration amended); types regenerated clean. +- Verification: lint bar green; seed established (fixtures + claimed test user); the test spike run + (adopted or dismissed → manual sign-off); e2e auth verified; RLS parity checked when a + user-client read surface exists. +- Legacy Foundation files deleted per ledger (`grids.service.ts` retained + annotated for Metering + re-home; Nest `GET /grids/:id` removed at close-out); all three registers updated. +- Records updated: ADR-013 authored + indexed; ADR-004 §5 & ADR-007 amended; roadmap + assumptions/index; deployment docs; ADR-005 flagged as 002e prerequisite. + +## Records to update (checklist) + +- [x] ADR-013 authored + AGENTS.md ADR index row (read-if-relevant; cursor rule not needed) +- [x] ADR-004 §5 amended (scope narrowings) +- [x] ADR-007 amended (wiring / deployment-group drop / boot model) +- [x] Roadmap `002-oss-migration.md`: sub-plan index, standing assumptions (module-split → ADR-013, + ADR-005 timing, no-cracks governance), notes log +- [x] Schema deviation register: `grids.timezone` (#35, Task 2) +- [x] Internationalization & de-brand register (created + seeded; Task 7 auth rows updated) +- [x] Deployment docs: Supabase required for foundation hosts, seed/bootstrap (Task 5; env + examples already updated in Task 4) +- [x] Import ledger (this file) kept current through Task 11 (Foundation deletes + grids annotate / + `GET /:id` removed; metering half pending) + +## Notes & decisions log + +> Append as the plan is executed. Format: `YYYY-MM-DD — [task] — note` + +- 2026-07-15 — Plan authored via an extended maintainer interview (see the oss-migration chat). + All "Decisions baked in" items were resolved collaboratively; this plan is the write-up. Division + of labor is per task/subtask, decided as we go. +- 2026-07-15 — [Task 1] Done & signed off. Decision records and registers written up front: + ADR-013, ADR-004 §5 + ADR-007 amendments, AGENTS.md ADR-index row, the + internationalization & de-brand register, and the roadmap updates (index, no-cracks governance, + assumptions 2/9/12, single-pass note). Next: Task 2 (`grids.timezone` → UTC — maintainer-owned). +- 2026-07-16 — [Task 3] Done. Reintroduced `supabase-types-adjusted.ts` (grids+poles + `location_geom`); `@nxt/core/types/supabase-types-adjusted` subpath export; import convention + documented (`libs/core/README.md` + module JSDoc); ESLint `no-restricted-imports` blocks + `Database` from generated subpath; golden-path probes in `health.service.ts`. Next: Task 4. +- 2026-07-16 — [Task 4 side] In-package imports: `@nxt/` across packages; within a + package use Node subpath `"imports"` `#config/`, `#modules/`, `#types/` (not `#/` — invalid + for TypeScript; see webpro.nl subpath-imports article). Direct files only — avoids + in-package barrel imports. Sibling `./` kept. Jest `moduleNameMapper` + `source`/`default` + conditions (`src`/`dist`) on `@nxt/core`. +- 2026-07-16 — [Task 4] Worker infra includes `GlobalHttpModule` (same as api) — workers are + integration-heavy; plan’s “api-only HTTP” narrowed at implementation. +- 2026-07-16 — [Task 4] Config lives only at `@nxt/core/config` (not re-exported from the fat + `@nxt/core` barrel). Bootstrap must not pull Nest modules when loading config. ESLint + `no-restricted-imports` blocks config symbols from `@nxt/core`. `GlobalLoggerModule` keeps + `forRootAsync` so option factories run at Nest init, after `loadConfig()`. +- 2026-07-16 — [Task 4] Prefer `constructor(logger: PinoLogger)` + `setContext` over + `@InjectPinoLogger(Service.name)`: nestjs-pino snapshots decorated tokens at + `LoggerModule.forRoot*` eval time; importing `GlobalLoggerModule` from another module file + (or barrel order) can register providers before those decorators run → missing + `PinoLogger:ServiceName`. `GlobalSupabaseModule` imports `GlobalLoggerModule` for DI. +- 2026-07-16 — [Task 4 / env] Local env files: root `.env` = shared; `apps//.env` = + host-specific. Nx loads project then workspace `.env` on `nx serve`/`build` (no + `dotenv-safe`; no mandatory `@nestjs/config` for local). Examples: + `.env.example`, `apps/api/.env.example`, `apps/worker/.env.example`. Production still + injects env via the platform. ADR-007 layer-2 `requireEnv` unchanged. +- 2026-07-16 — [Task 4 / env] Supabase env names → current terminology: + `SUPABASE_URL` + `SUPABASE_SECRET_KEY` (admin client, both hosts); + `SUPABASE_PUBLISHABLE_KEY` + `SUPABASE_JWKS_URL` (auth, api / Task 7); + `SUPABASE_JWT_SECRET` kept as documented legacy HS256 fallback only. + Local CLI may still print anon/service_role labels; values work under either naming. + Admin `createClient` options: `persistSession: false`, `autoRefreshToken: false`. +- 2026-07-16 — [Task 4 / logging] Keep nestjs-pino; `autoLogging: false` always (no HTTP + request dumps). `LOG_PRETTY` opt-in only (default off) so `console.*` stays usable next + to JSON Nest/Pino logs. +- 2026-07-16 — [Task 4 / logging] Deferred nestjs-pino: webpack serve + transport workers + break pretty (“log once then silence”); console DX suffered. `GlobalLoggerModule` is a + no-op stub still wired in both hosts’ `infrastructure`; `logger.options.ts` retained for + one-place restore. Hosts use Nest `Logger` / `console.*` until structured logging returns. +- 2026-07-16 — [Task 4 / 4.4c] Dropped `deployment` from config schema + default/example + + fixtures/specs. Removed scaffold golden-path proofs (`getPackageInfo`, type probes on + HealthService); `/health` keeps a lightweight Supabase `organizations` probe. +- 2026-07-16 — [Task 4] **Done & signed off.** Infra + composition complete (see Task 4 Done + block). Next: Task 5 (seed harness). +- 2026-07-16 — [Task 5] Seed harness established: `supabase/seed.sql` (orgs/wallets, two auth + users with claims + members, api key, solar grid); documented in + `docs/deployment/supabase.md` §5 as local-only (`pnpm exec supabase db reset`). **Done — + awaiting sign-off.** Next: Task 6 (`accounts` + `api-keys`). +- 2026-07-16 — [Task 6 / typing] **Do not** hand-build nested relation response types up front. + Typed Supabase client infers select return types. Prefer inference at the call site; write a + narrow manual type only when a method’s contract needs it; for complex selects use + `QueryData` (see legacy `meter-interactions/lib/supabase.ts`). Reverted an + early `*.types.ts` draft that reconstructed joins. +- 2026-07-16 — [Task 6 / accounts] **`accounts` is type-only** (same posture as `members`). Sole + legacy caller was `supabase.strategy` `@TEMPORARY` Account attach; Foundation already uses + `account_id` / JWT claims. No `AccountsService` / accounts Nest module in OSS. Task 7: drop + `account` from `AuthenticatedUser`. +- 2026-07-16 — [Task 6 / api-keys] **No standalone `ApiKeysService`.** Sole caller is + `ApiKeyStrategy` → absorb into Task 7: inject `SupabaseService` admin client and select at the + call site. Task 6 is **docs/ledger only**. Legacy `accounts` / `api-keys` / `members` files + remain under `legacy/` until **Task 11** deletes fully superseded Foundation sources. +- 2026-07-16 — [Task 6] **Done (docs-only) — awaiting sign-off.** Next: Task 7 (`auth`). +- 2026-07-17 — [Task 7 / security] ADR-014 authored: machine credentials (API keys, scopes, + Postgres readonly roles vs Supabase JWT roles, MCP team vs customer tenancy, hardening order). + Public by design (architecture for adopters); no secrets. +- 2026-07-16 — [Task 7 / deps] Minimal auth packages on `api`: `@nestjs/passport`, `passport` + (peer), `passport-http-bearer`, `passport-headerapikey`, `jose` (JWKS + verify), + `@supabase/supabase-js` (user client + types on the host). **No** `@nestjs/jwt` / + `jsonwebtoken` / `jwks-rsa`. Grafana RS256 endpoint skipped. Small `/auth/me` probe planned + for early JWKS verification. +- 2026-07-17 — [Task 7 / bootstrap] `enableCors()` + global `ValidationPipe` on `api` only; + `class-validator` / `class-transformer` on `@nxt/api` only (not core/worker — avoids dragging + Nest optional peers into every host). `throwSupabaseError` takes a structural logger so pnpm + dual `@nestjs/common` peer installs do not break typecheck across packages. +- 2026-07-17 — [Task 7 / ledger] Auth marked **imported**; api-keys rows note inline strategy + destination. i18n/de-brand register #2–#3 updated (destinations). Seed verify still open. +- 2026-07-17 — [Task 7] **Done — awaiting sign-off.** Maintainer verified seeded bearer + + `X-API-KEY` via httpYac `/auth/me`. Next: Task 8 (scoped test spike keep-or-dismiss). +- 2026-07-17 — [Task 7] **Signed off.** Next: Task 8. +- 2026-07-17 — [Task 8] **Adopted** (keep). Layout: `apps/api/test/{unit,integration,e2e,helpers}`; + default `nx test api` = unit only; `api:test-integration` / `api:test-e2e` opt-in + skip without + `SUPABASE_*`. Spike green: ApiKeyStrategy integration + thin `GET /auth/me` X-API-KEY e2e (not + full `AppModule` — `jose` ESM/Jest). Full-app e2e deferred; don’t proliferate thin e2e modules. + Bearer → httpYac. Next: Task 9. +- 2026-07-17 — [Task 8] **Signed off.** Next: Task 9 (`organizations` + `user-admin`). +- 2026-07-17 — [Task 8 / follow-up] `libs/core` specs moved to `libs/core/test/unit/` (mirrors api; + libs = unit-only by convention). +- 2026-07-17 — [Task 9] Nest **`organizations` skipped** (dead surface). `user-admin` imported to + `apps/api` with whole-method admin client; `CreateCustomerDto` in `@nxt/core` (`class-validator` + dep on core for shared DTO). Dead test-user block not ported. Maintainer local serve smoke OK. +- 2026-07-17 — [Task 9 / API keys] Confirmed gap: API-key auth attaches claims but **no** + per-request user client → machine handlers that need PostgREST fall through to `service_role`. + **Near-future (before more machine data paths):** implement ADR-014 §5.3 — after key validation, + attach an `authenticated` client with the account’s JWT claims. Privileged Auth Admin surfaces + (`user-admin`) stay whole-method admin regardless. Comment left on `ApiKeyStrategy`. +- 2026-07-17 — [Task 9 / Supabase helpers] `SupabaseService.handleSingle` for `.single()` (value or + throw); `handleResponse` stays permissive for `.maybeSingle()` / lists. Cloudflare HTML 5xx + **throws** (no soft-`null`); `isCloudflareHtmlError` (file-local) collapses the HTML body to a + one-liner and maps to **503 Service Unavailable** — amends Task 4 soft-return. Rationale: legacy + soft-`null` was a panic brake when handlers/jobs were not throw-ready; empty-data lied about + outages and broke `.single()` contracts. Retries for this infra class = **far-future** (not in + 002d). +- 2026-07-17 — [Task 9] **Signed off.** Next: Task 10 (`grids` partial, per ADR-013). +- 2026-07-17 — [Task 10] **Nest `grids` skipped** (docs/ledger only; organizations posture). Usage + audit: `findOne` / `GET /:id` ≈ Flow XO only → delete at Task 11, not port; `updateMany` / + `PUT /grids` served loch weather under the old api-only ops-DB write funnel; org list / + `findAll` / create had no real Nest consumers. **Ops-DB write funnel dropped:** hosts/modules + may write operations data directly (self-contained); do not reintroduce “only api writes.” + Metering-coupled grid methods remain ledger **re-home to Metering, pending**. Grid table + + RLS + seed stay Foundation. User-client-by-default smoke deferred to the next real bearer + read. **Done (docs-only) — signed off.** Next: Task 11 (close-out). +- 2026-07-17 — [Task 11] Started. Grids close-out: **annotate** service; **remove only** + `GET /grids/:id` from the controller (keep `PUT` / connectivity / download). Do not strip + `findOne` from the service (legacy dcus/payouts/lost-revenue callers). +- 2026-07-17 — [Task 11 / deletes] Removed superseded Foundation from `legacy/`: supabase + + logger-module + global-http; accounts / api-keys / members / organizations (core + tiamat); + auth + user-admin; create-customer.dto; supabase-types-adjusted. Grids: annotated services; + controller kept minus `GET /:id`; unwired deleted Foundation modules from tiamat `AppModule`. + Legacy `@core` barrel trimmed. +- 2026-07-17 — [Task 11] **Done & signed off.** Exit bar met: lint bar green; `demo`/`deployment` + gone; ledger finalized; roadmap 002d → **Completed**. Next family step was: lock ADR-005, then + author **002e**. +- 2026-07-17 — **ADR-005 Accepted** (recorded on parent roadmap). 002e authoring unblocked. diff --git a/docs/plans/002-oss-migration/internationalization-and-debrand-register.md b/docs/plans/002-oss-migration/internationalization-and-debrand-register.md new file mode 100644 index 0000000..ed024ce --- /dev/null +++ b/docs/plans/002-oss-migration/internationalization-and-debrand-register.md @@ -0,0 +1,56 @@ +# Internationalization & De-Brand Register + +**Companion to:** the OSS migration roadmap `docs/plans/002-oss-migration.md` (migration-wide — +**not** tied to a single sub-plan). +**Purpose:** the authoritative record of every **Nigeria-specific / NXT-Grid-specific value or +name** found in the legacy code and schema, and how the OSS baseline neutralizes it. This is +roadmap **rule 6** ("keep an eye out for the platform becoming international instead of +Nigeria-only") made concrete and durable. + +Two flavors live here: + +- **i18n** — hardcoded locale assumptions: currency, timezone, phone/country, language, date/number + formatting, tax/regulatory constants. +- **de-brand** — hardcoded company identity: the "NXT Grid" name, `nxtgrid.co` emails/domains, brand + strings, brand-named symbols in code (types, flags, constants). + +**Rules:** + +- One row per finding. A de-brand/i18n change ships in an import only if it has a row here. +- `Disposition` is one of: `neutralized` (fixed in baseline), `configurable` (moved to config/env, + with the key), `deferred` (returns with its owning capability — record where), `noted` (future + candidate, no action yet). +- **Schema-level** findings (a column default, an enum, a constraint) are *also* recorded in the + **schema deviation register** (they have a cutover implication); this register cross-links them so + the i18n picture is complete in one place. +- Status: `candidate` (spotted, unconfirmed) → `confirmed` (maintainer sign-off) or `rejected` + (kept as-is, row stays for the record). +- Appended by every capability import that touches locale/brand values. If in doubt, add a row. + +## Register + +| # | Location | Finding | Kind | Disposition | Cross-ref | Status | +|---|---|---|---|---|---|---| +| 1 | `grids.timezone` column default (002b init migration; legacy `grid.entity.ts` `'Africa/Lagos'`) | Timezone defaults to Lagos — a Nigeria assumption baked into the **schema**, not just code | i18n | **neutralized** — default changed to `'UTC'` in the init migration (002d Task 2). New inserts only; existing company grids keep stored values | Schema deviation register #35 | confirmed | +| 2 | `legacy/apps/tiamat/src/modules/auth/nxt-supabase-user.ts` — type `NxtSupabaseUser` | Brand-named auth user type (`Nxt…`) | de-brand | **neutralized** — `apps/api/src/modules/auth/authenticated-user.ts` exports `AuthenticatedUser` (002d Task 7) | 002d Task 7 | confirmed | +| 3 | `legacy/apps/tiamat/src/modules/auth/supabase.strategy.ts` — comment "let all NXT Grid pass" + `console.info` | Brand reference in comment; also the (deferred) admin-org membership flag `is_nxt_grid_member` | de-brand | **neutralized** — brand comment not ported; Nest `Logger` instead of `console.*`; flag **deferred** (no in-scope reader), reintroduce as `is_admin_org_member` with its consumer | 002d Task 7; ADR-007 Amendment 2026-07-15 §B | confirmed | +| 4 | `legacy/apps/tiamat/src/modules/user-admin/user-admin.service.ts` — commented-out test-user block (`bobby.bol@nxtgrid.co`, `+31…` phone) + "Create a NXT Grid member" comment | Brand emails/phone in dead code; brand reference in comment | de-brand | **neutralized** — dead block deleted (not ported); comment de-branded on import (`apps/api/.../user-admin/`) | 002d Task 9 | confirmed | +| 5 | `organizations` (no `timezone` column today) | An org-level timezone would be the natural i18n home once multi-region operators exist (grids currently carry timezone) | i18n | **noted** — future candidate; no action in 002d | — | candidate | + +## Notes + +> Append context/decisions here as the register grows. + +- 2026-07-15 — Register created during 002d Task 1. Seeded from the foundation-scope de-brand/i18n + sweep (chat, 2026-07-15). The one substantive **schema** i18n hit is `grids.timezone` (#1); the + rest are code-level de-brand items handled inline during their module's import. Currency was **not** + found hardcoded in the foundation scope — expect currency findings to surface in the Payments + import; add them here then. +- 2026-07-16 — [002d Task 2] Init migration amended (`grids.timezone` default → `'UTC'`); schema + deviation register #35 added. Maintainer sign-off 2026-07-16 after `db reset` + + `generate-types:local` (empty diff on `supabase-types.ts` as expected). +- 2026-07-17 — [002d Task 7] Auth de-brand rows #2–#3 updated with OSS destinations. Status stays + `candidate` until Task 7 seed verify + maintainer sign-off (then → `confirmed`). +- 2026-07-17 — [002d Task 7] Seed verify passed (maintainer); rows #2–#3 → `confirmed`. +- 2026-07-17 — [002d Task 9] `user-admin` imported; row #4 → `confirmed` (dead brand block not + ported; local serve smoke OK). diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..57e8963 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,144 @@ +import nx from '@nx/eslint-plugin'; + +/** Team style + pragmas ported from legacy/eslint.config.ts (rules block only). */ +const teamRules = { + 'no-debugger': 'off', + 'no-console': ['warn', { allow: ['info', 'warn', 'error'] }], + 'one-var': ['warn', 'never'], + eqeqeq: ['warn', 'smart'], + 'id-length': ['warn', { min: 2, properties: 'never', exceptions: ['i'] }], + 'dot-notation': 'warn', + semi: ['warn', 'always'], + indent: ['warn', 2, { SwitchCase: 1 }], + 'no-multiple-empty-lines': 'warn', + quotes: ['warn', 'single'], + 'comma-dangle': [ + 'warn', + { + arrays: 'always-multiline', + objects: 'always-multiline', + imports: 'always-multiline', + exports: 'always-multiline', + functions: 'always-multiline', + }, + ], + 'array-bracket-spacing': ['warn', 'always'], + 'object-curly-spacing': ['warn', 'always'], + 'template-curly-spacing': ['warn', 'always'], + 'arrow-parens': ['warn', 'as-needed'], + 'brace-style': ['warn', 'stroustrup', { allowSingleLine: true }], + 'no-trailing-spaces': 'warn', + 'eol-last': 'warn', + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-require-imports': 'off', + 'no-undef': 'off', + 'no-warning-comments': 'off', + '@typescript-eslint/no-unused-vars': [ + 'warn', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + }, + ], + 'no-restricted-imports': [ + 'error', + { + paths: [ + { + name: '@nxt/core/types/supabase-types', + importNames: ['Database'], + message: + 'Import Database from @nxt/core/types/supabase-types-adjusted; use supabase-types for enums and row aliases only.', + }, + { + name: '@nxt/core', + importNames: [ + 'getConfig', + 'loadConfig', + 'setConfig', + 'requireEnv', + 'NxtConfig', + 'LoadConfigOptions', + ], + message: + 'Import config from @nxt/core/config — the fat @nxt/core barrel must not load Nest modules during bootstrap.', + }, + ], + }, + ], +}; + +export default [ + ...nx.configs['flat/base'], + ...nx.configs['flat/typescript'], + ...nx.configs['flat/javascript'], + { + ignores: [ + '**/dist', + '**/out-tsc', + '**/coverage', + '**/node_modules', + '**/.nx/**', + '**/generated/**', + 'legacy/**', + 'supabase/**', + '**/main.js', + '**/*.js.map', + '**/*.d.ts', + '**/*.d.ts.map', + '**/*.spec.js', + 'test-output/**', + '**/eslint.config.mjs', + 'eslint.config.mjs', + 'jest.config.ts', + 'jest.preset.js', + ], + }, + { + files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], + rules: { + '@nx/enforce-module-boundaries': [ + 'error', + { + enforceBuildableLibDependency: true, + allow: [ + '^.*/eslint(\\.base)?\\.config\\.[cm]?[jt]s$', + // In-package Node subpath imports (package.json "imports") — not the barrel. + '#config/**', + '#modules/**', + '#types/**', + ], + depConstraints: [ + { + sourceTag: '*', + onlyDependOnLibsWithTags: ['*'], + }, + ], + }, + ], + }, + }, + { + files: [ + '**/*.ts', + '**/*.tsx', + '**/*.cts', + '**/*.mts', + '**/*.js', + '**/*.jsx', + '**/*.cjs', + '**/*.mjs', + ], + rules: teamRules, + }, + { + files: ['**/*.config.js', 'jest.preset.js', '**/webpack.config.js'], + languageOptions: { + sourceType: 'script', + }, + rules: { + '@typescript-eslint/no-require-imports': 'off', + }, + }, +]; diff --git a/jest.config.ts b/jest.config.ts index 7a2902f..9e3d1db 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -1,5 +1,6 @@ +import type { Config } from 'jest'; import { getJestProjectsAsync } from '@nx/jest'; -export default { - projects: getJestProjectsAsync(), -}; +export default async (): Promise => ({ + projects: await getJestProjectsAsync() +}); \ No newline at end of file diff --git a/jest.preset.js b/jest.preset.js index f078ddc..da8e8c2 100644 --- a/jest.preset.js +++ b/jest.preset.js @@ -1,3 +1,3 @@ const nxPreset = require('@nx/jest/preset').default; -module.exports = { ...nxPreset }; +module.exports = { ...nxPreset }; \ No newline at end of file diff --git a/legacy/README.md b/legacy/README.md new file mode 100644 index 0000000..4e7fd4b --- /dev/null +++ b/legacy/README.md @@ -0,0 +1,283 @@ +## Legacy Reference Tree + +This folder is a frozen, read-only reference of the pre-migration codebase that existed before the +open-source re-scaffold began. + +- It is not built, installed, or run. +- It is excluded from the active workspace, CI, and toolchains. +- Files in this folder are never edited. +- Files are deleted from this folder one by one only after they are fully superseded in the new + workspace. +- To observe legacy behavior live, use the private company repository. + +# NXT backend + +**NXT backend** is an open-source backend platform for managing off-grid and mini-grid electricity systems. It handles payment processing, remote monitoring of production and distribution infrastructure, and remote interaction with smart prepaid electricity meters. + +The platform is designed for organizations that operate mini-grids in emerging markets — enabling them to manage customers, meters, grids, payouts, and real-time energy data from a single system. + +--- + +## Table of Contents + +- [Architecture](#architecture) +- [Apps](#apps) +- [Libraries](#libraries) +- [Tech Stack](#tech-stack) +- [Prerequisites](#prerequisites) +- [Getting Started](#getting-started) +- [Environment Variables](#environment-variables) +- [Supabase](#supabase) +- [Building and Running Apps](#building-and-running-apps) +- [Type Checking and Linting](#type-checking-and-linting) +- [Known Issues](#known-issues) +- [License](#license) + +--- + +## Architecture + +NXT backend is structured as an **Nx monorepo** containing multiple NestJS backend applications and shared libraries. Each application has a distinct responsibility: + +``` +nxt-backend/ +├── apps/ +│ ├── tiamat/ # Main API — operations, payments, meters, users +│ ├── talos/ # Meter provisioning and hardware registration +│ ├── loch/ # Background jobs, automation, and integrations +│ └── yeti/ # Time-series data collection and grid monitoring +├── libs/ +│ ├── core/ # Shared NestJS modules (auth, alerts, etc.) +│ ├── helpers/ # Shared utility functions +│ └── timeseries/ # TimescaleDB integration layer +└── supabase/ # Database migrations, functions, and seed data +``` + +Authentication is handled via **Supabase Auth**. The primary database is **Supabase (PostgreSQL)**. Time-series data (snapshots, energy readings, forecasts) is stored in a separate **TimescaleDB** instance. + +--- + +## Apps + +### `tiamat` — Main Operations API + +The primary REST + WebSocket API consumed by the frontend dashboards. Covers: + +- **Meter management** — remote meter interactions, device messages, install sessions, directive batches +- **Grid infrastructure** — grids, distribution control units (DCUs), MPPTs +- **Payments** — orders, wallets, payouts, Flutterwave integration +- **Users & organizations** — multi-tenancy, user admin, roles +- **Notifications** — email (SendGrid), SMS (Africa's Talking), Telegram, USSD sessions +- **Field data collection** — EpiCollect integration +- **LoRaWAN** — ChirpStack network server integration +- **Automation** — autopilot, directive watchdog +- **Reporting** — data analytics, CSV/XLSX downloads + +### `talos` — Meter Provisioning Service + +Handles meter hardware registration and provisioning workflows. Currently supports **CALIN** meters (v1 and v2 API). Designed to accommodate additional meter brands in the future. + +### `loch` — Background Jobs & Integrations + +Long-running background service responsible for: + +- **MQTT monitoring** — subscribes to Victron Energy MQTT brokers for live battery/solar state +- **Automated jobs** — lifeline credit top-ups, revenue updates, autopilot triggers +- **Notification dispatch** — outbound email, SMS, Telegram +- **Third-party integrations** — Make.com automation webhooks, weather data, sunrise/sunset scheduling +- **EpiCollect** — inbound field survey data processing + +### `yeti` — Time-Series Data Collector + +Periodic data collection and aggregation service writing to TimescaleDB: + +- **Snapshots** — meter, grid, MPPT, router, DCU at intervals from 1 minute to 1 day +- **Forecasting** — solar irradiance via Solcast integration +- **Digital twin** — live grid state representation +- **Grid diagnostics** — health and connectivity monitoring +- **Exchange rates** — daily currency rate snapshots + +--- + +## Libraries + +### `core` +Shared NestJS modules used across apps: authentication guards, software dev alerting, and other cross-cutting concerns. + +### `helpers` +Shared utility functions and TypeScript helpers. + +### `timeseries` +Integration layer for TimescaleDB — connection management and shared query utilities. + +--- + +## Tech Stack + +| Layer | Technology | +|---|---| +| Framework | [NestJS](https://nestjs.com/) | +| Monorepo tooling | [Nx](https://nx.dev/) | +| Language | TypeScript | +| Primary database | [Supabase](https://supabase.com/) (PostgreSQL) | +| Time-series database | [TimescaleDB](https://www.timescale.com/) via [TypeORM](https://typeorm.io/) | +| Authentication | Supabase Auth | +| Real-time | WebSockets (Socket.IO) | +| Messaging | MQTT | +| Email | SendGrid | +| SMS | Africa's Talking | +| Payments | Flutterwave | +| Solar forecasting | Solcast | +| LoRaWAN | ChirpStack | +| Meter hardware | CALIN (v1 + v2 API) | + +> **Note on TypeORM:** You will find TypeORM used in two distinct contexts in this codebase. +> The integration with **TimescaleDB** uses TypeORM intentionally and permanently — it is the chosen ORM for that database and will remain so. +> The integration with the **primary Supabase (PostgreSQL) database**, however, is legacy: the project originally used TypeORM there and has been gradually migrating to the Supabase client. That migration is incomplete, so some modules still use TypeORM entities and repositories against the main database. This is known technical debt and contributions that continue the migration are welcome. + +--- + +## Prerequisites + +- [Node.js](https://nodejs.org/) (check `.nvmrc` or `package.json` for the required version) +- [Docker](https://www.docker.com/) (required for running Supabase locally) +- [Supabase CLI](https://supabase.com/docs/guides/cli) +- [Nx CLI](https://nx.dev/getting-started/intro) (optional — can use `npx nx`) + +--- + +## Getting Started + +```bash +# Install dependencies +npm install + +# Start Supabase locally (requires Docker) +npx supabase start + +# Copy and fill in environment variables +cp apps/tiamat/.env.example apps/tiamat/.env +cp apps/talos/.env.example apps/talos/.env +cp apps/loch/.env.example apps/loch/.env +cp apps/yeti/.env.example apps/yeti/.env + +# Run the main API +npx nx serve tiamat +``` + +--- + +## Environment Variables + +Each app has a `.env.example` file listing the required environment variables with descriptions. Copy each one to `.env` and fill in the values for your environment before running. + +All secrets (API keys, database credentials, passwords) must be provided via environment variables — no credentials are hardcoded in the codebase. + +--- + +## Supabase + +### Start a local Supabase session +```bash +npx supabase start +``` + +### Pull the latest database schema from production +```bash +npx supabase db pull +``` + +### Reset the local database +```bash +npx supabase db reset +``` + +### Generate TypeScript types + +From a local Supabase instance: +```bash +npm run generate-types:local +``` + +From a remote/production Supabase project: +```bash +npx supabase login # if not already logged in +npm run generate-types +``` + +> **Note:** The generated `Json` type is automatically post-processed to `Record` instead of the default recursive union type, allowing TypeScript to properly access properties on JSON fields. The script `.scripts/fix-json-type.js` handles this automatically during type generation. + +--- + +## Building and Running Apps + +### Build an app +```bash +npx nx build + +# Examples: +npx nx build tiamat +npx nx build talos +npx nx build loch +npx nx build yeti + +# Build with production configuration +npx nx build --configuration=production +``` + +### Run an app (development) +```bash +npx nx serve + +# Examples: +npx nx serve tiamat +npx nx serve talos +npx nx serve loch +npx nx serve yeti + +# Run with production configuration +npx nx serve --configuration=production +``` + +--- + +## Type Checking and Linting + +```bash +# Type checking only +npm run check-types + +# Linting only +npm run eslint + +# Both +npm run lint +``` + +--- + +## Known Issues + +Older packages are causing high CPU usage in some environments. If the Nx process keeps running in the background after stopping a serve command: + +```bash +# Find the process +ps aux | grep nx + +# Kill it +kill -9 +``` + +Alternatively, check Activity Monitor (macOS) or Task Manager (Windows) for a lingering `nx` process. + +--- + +## License + +NXT backend is licensed under the [Mozilla Public License 2.0](https://www.mozilla.org/MPL/2.0/). See [LICENSE](./LICENSE) for the full text. + +--- + +## Authors + +NXT backend was created by **Bobby Bol** and **Tommaso Girotto**. See [AUTHORS.md](./AUTHORS.md) for details. diff --git a/apps/loch/.env.example b/legacy/apps/loch/.env.example similarity index 100% rename from apps/loch/.env.example rename to legacy/apps/loch/.env.example diff --git a/apps/loch/README.md b/legacy/apps/loch/README.md similarity index 100% rename from apps/loch/README.md rename to legacy/apps/loch/README.md diff --git a/apps/loch/jest.config.ts b/legacy/apps/loch/jest.config.ts similarity index 100% rename from apps/loch/jest.config.ts rename to legacy/apps/loch/jest.config.ts diff --git a/apps/loch/project.json b/legacy/apps/loch/project.json similarity index 100% rename from apps/loch/project.json rename to legacy/apps/loch/project.json diff --git a/apps/loch/src/main.ts b/legacy/apps/loch/src/main.ts similarity index 100% rename from apps/loch/src/main.ts rename to legacy/apps/loch/src/main.ts diff --git a/apps/loch/src/modules/africastalking/africastalking.module.ts b/legacy/apps/loch/src/modules/africastalking/africastalking.module.ts similarity index 100% rename from apps/loch/src/modules/africastalking/africastalking.module.ts rename to legacy/apps/loch/src/modules/africastalking/africastalking.module.ts diff --git a/apps/loch/src/modules/africastalking/africastalking.service.ts b/legacy/apps/loch/src/modules/africastalking/africastalking.service.ts similarity index 100% rename from apps/loch/src/modules/africastalking/africastalking.service.ts rename to legacy/apps/loch/src/modules/africastalking/africastalking.service.ts diff --git a/apps/loch/src/modules/agents/agents.module.ts b/legacy/apps/loch/src/modules/agents/agents.module.ts similarity index 100% rename from apps/loch/src/modules/agents/agents.module.ts rename to legacy/apps/loch/src/modules/agents/agents.module.ts diff --git a/apps/loch/src/modules/app.module.ts b/legacy/apps/loch/src/modules/app.module.ts similarity index 100% rename from apps/loch/src/modules/app.module.ts rename to legacy/apps/loch/src/modules/app.module.ts diff --git a/apps/loch/src/modules/autopilot/autopilot.controller.ts b/legacy/apps/loch/src/modules/autopilot/autopilot.controller.ts similarity index 100% rename from apps/loch/src/modules/autopilot/autopilot.controller.ts rename to legacy/apps/loch/src/modules/autopilot/autopilot.controller.ts diff --git a/apps/loch/src/modules/autopilot/autopilot.module.ts b/legacy/apps/loch/src/modules/autopilot/autopilot.module.ts similarity index 100% rename from apps/loch/src/modules/autopilot/autopilot.module.ts rename to legacy/apps/loch/src/modules/autopilot/autopilot.module.ts diff --git a/apps/loch/src/modules/autopilot/autopilot.service.ts b/legacy/apps/loch/src/modules/autopilot/autopilot.service.ts similarity index 100% rename from apps/loch/src/modules/autopilot/autopilot.service.ts rename to legacy/apps/loch/src/modules/autopilot/autopilot.service.ts diff --git a/apps/loch/src/modules/autopilot/dto/autopilot.matrix.input.ts b/legacy/apps/loch/src/modules/autopilot/dto/autopilot.matrix.input.ts similarity index 100% rename from apps/loch/src/modules/autopilot/dto/autopilot.matrix.input.ts rename to legacy/apps/loch/src/modules/autopilot/dto/autopilot.matrix.input.ts diff --git a/apps/loch/src/modules/autopilot/quokka.ts b/legacy/apps/loch/src/modules/autopilot/quokka.ts similarity index 100% rename from apps/loch/src/modules/autopilot/quokka.ts rename to legacy/apps/loch/src/modules/autopilot/quokka.ts diff --git a/apps/loch/src/modules/epicollect/epicollect.controller.ts b/legacy/apps/loch/src/modules/epicollect/epicollect.controller.ts similarity index 100% rename from apps/loch/src/modules/epicollect/epicollect.controller.ts rename to legacy/apps/loch/src/modules/epicollect/epicollect.controller.ts diff --git a/apps/loch/src/modules/epicollect/epicollect.module.ts b/legacy/apps/loch/src/modules/epicollect/epicollect.module.ts similarity index 100% rename from apps/loch/src/modules/epicollect/epicollect.module.ts rename to legacy/apps/loch/src/modules/epicollect/epicollect.module.ts diff --git a/apps/loch/src/modules/epicollect/epicollect.service.ts b/legacy/apps/loch/src/modules/epicollect/epicollect.service.ts similarity index 100% rename from apps/loch/src/modules/epicollect/epicollect.service.ts rename to legacy/apps/loch/src/modules/epicollect/epicollect.service.ts diff --git a/apps/loch/src/modules/epicollect/types/survey-type.ts b/legacy/apps/loch/src/modules/epicollect/types/survey-type.ts similarity index 100% rename from apps/loch/src/modules/epicollect/types/survey-type.ts rename to legacy/apps/loch/src/modules/epicollect/types/survey-type.ts diff --git a/apps/loch/src/modules/lifeline/lifeline.module.ts b/legacy/apps/loch/src/modules/lifeline/lifeline.module.ts similarity index 100% rename from apps/loch/src/modules/lifeline/lifeline.module.ts rename to legacy/apps/loch/src/modules/lifeline/lifeline.module.ts diff --git a/apps/loch/src/modules/lifeline/lifeline.service.ts b/legacy/apps/loch/src/modules/lifeline/lifeline.service.ts similarity index 100% rename from apps/loch/src/modules/lifeline/lifeline.service.ts rename to legacy/apps/loch/src/modules/lifeline/lifeline.service.ts diff --git a/apps/loch/src/modules/make/make.module.ts b/legacy/apps/loch/src/modules/make/make.module.ts similarity index 100% rename from apps/loch/src/modules/make/make.module.ts rename to legacy/apps/loch/src/modules/make/make.module.ts diff --git a/apps/loch/src/modules/mongo-atlas/mongo-atlas.module.ts b/legacy/apps/loch/src/modules/mongo-atlas/mongo-atlas.module.ts similarity index 100% rename from apps/loch/src/modules/mongo-atlas/mongo-atlas.module.ts rename to legacy/apps/loch/src/modules/mongo-atlas/mongo-atlas.module.ts diff --git a/apps/loch/src/modules/mongo-atlas/mongo-atlas.service.ts b/legacy/apps/loch/src/modules/mongo-atlas/mongo-atlas.service.ts similarity index 100% rename from apps/loch/src/modules/mongo-atlas/mongo-atlas.service.ts rename to legacy/apps/loch/src/modules/mongo-atlas/mongo-atlas.service.ts diff --git a/apps/loch/src/modules/mqtt/mqtt.module.ts b/legacy/apps/loch/src/modules/mqtt/mqtt.module.ts similarity index 100% rename from apps/loch/src/modules/mqtt/mqtt.module.ts rename to legacy/apps/loch/src/modules/mqtt/mqtt.module.ts diff --git a/apps/loch/src/modules/mqtt/mqtt.service.ts b/legacy/apps/loch/src/modules/mqtt/mqtt.service.ts similarity index 100% rename from apps/loch/src/modules/mqtt/mqtt.service.ts rename to legacy/apps/loch/src/modules/mqtt/mqtt.service.ts diff --git a/apps/loch/src/modules/notification-parameters/notification-parameters.module.ts b/legacy/apps/loch/src/modules/notification-parameters/notification-parameters.module.ts similarity index 100% rename from apps/loch/src/modules/notification-parameters/notification-parameters.module.ts rename to legacy/apps/loch/src/modules/notification-parameters/notification-parameters.module.ts diff --git a/apps/loch/src/modules/notification-parameters/notification-parameters.service.ts b/legacy/apps/loch/src/modules/notification-parameters/notification-parameters.service.ts similarity index 100% rename from apps/loch/src/modules/notification-parameters/notification-parameters.service.ts rename to legacy/apps/loch/src/modules/notification-parameters/notification-parameters.service.ts diff --git a/apps/loch/src/modules/notifications/notifications.controller.ts b/legacy/apps/loch/src/modules/notifications/notifications.controller.ts similarity index 100% rename from apps/loch/src/modules/notifications/notifications.controller.ts rename to legacy/apps/loch/src/modules/notifications/notifications.controller.ts diff --git a/apps/loch/src/modules/notifications/notifications.module.ts b/legacy/apps/loch/src/modules/notifications/notifications.module.ts similarity index 100% rename from apps/loch/src/modules/notifications/notifications.module.ts rename to legacy/apps/loch/src/modules/notifications/notifications.module.ts diff --git a/apps/loch/src/modules/notifications/notifications.service.ts b/legacy/apps/loch/src/modules/notifications/notifications.service.ts similarity index 100% rename from apps/loch/src/modules/notifications/notifications.service.ts rename to legacy/apps/loch/src/modules/notifications/notifications.service.ts diff --git a/apps/loch/src/modules/payouts/payouts.module.ts b/legacy/apps/loch/src/modules/payouts/payouts.module.ts similarity index 100% rename from apps/loch/src/modules/payouts/payouts.module.ts rename to legacy/apps/loch/src/modules/payouts/payouts.module.ts diff --git a/apps/loch/src/modules/payouts/payouts.service.ts b/legacy/apps/loch/src/modules/payouts/payouts.service.ts similarity index 100% rename from apps/loch/src/modules/payouts/payouts.service.ts rename to legacy/apps/loch/src/modules/payouts/payouts.service.ts diff --git a/apps/loch/src/modules/pd-hero/pd-hero.controller.ts b/legacy/apps/loch/src/modules/pd-hero/pd-hero.controller.ts similarity index 100% rename from apps/loch/src/modules/pd-hero/pd-hero.controller.ts rename to legacy/apps/loch/src/modules/pd-hero/pd-hero.controller.ts diff --git a/apps/loch/src/modules/pd-hero/pd-hero.module.ts b/legacy/apps/loch/src/modules/pd-hero/pd-hero.module.ts similarity index 100% rename from apps/loch/src/modules/pd-hero/pd-hero.module.ts rename to legacy/apps/loch/src/modules/pd-hero/pd-hero.module.ts diff --git a/apps/loch/src/modules/pd-hero/pd-hero.service.ts b/legacy/apps/loch/src/modules/pd-hero/pd-hero.service.ts similarity index 100% rename from apps/loch/src/modules/pd-hero/pd-hero.service.ts rename to legacy/apps/loch/src/modules/pd-hero/pd-hero.service.ts diff --git a/apps/loch/src/modules/revenue-update/revenue-update.module.ts b/legacy/apps/loch/src/modules/revenue-update/revenue-update.module.ts similarity index 100% rename from apps/loch/src/modules/revenue-update/revenue-update.module.ts rename to legacy/apps/loch/src/modules/revenue-update/revenue-update.module.ts diff --git a/apps/loch/src/modules/revenue-update/revenue-update.service.ts b/legacy/apps/loch/src/modules/revenue-update/revenue-update.service.ts similarity index 100% rename from apps/loch/src/modules/revenue-update/revenue-update.service.ts rename to legacy/apps/loch/src/modules/revenue-update/revenue-update.service.ts diff --git a/apps/loch/src/modules/sendgrid/sendgrid.module.ts b/legacy/apps/loch/src/modules/sendgrid/sendgrid.module.ts similarity index 100% rename from apps/loch/src/modules/sendgrid/sendgrid.module.ts rename to legacy/apps/loch/src/modules/sendgrid/sendgrid.module.ts diff --git a/apps/loch/src/modules/sendgrid/sendgrid.service.ts b/legacy/apps/loch/src/modules/sendgrid/sendgrid.service.ts similarity index 100% rename from apps/loch/src/modules/sendgrid/sendgrid.service.ts rename to legacy/apps/loch/src/modules/sendgrid/sendgrid.service.ts diff --git a/apps/loch/src/modules/sunrise-sunset/sunrise-sunset.module.ts b/legacy/apps/loch/src/modules/sunrise-sunset/sunrise-sunset.module.ts similarity index 100% rename from apps/loch/src/modules/sunrise-sunset/sunrise-sunset.module.ts rename to legacy/apps/loch/src/modules/sunrise-sunset/sunrise-sunset.module.ts diff --git a/apps/loch/src/modules/sunrise-sunset/sunrise-sunset.service.ts b/legacy/apps/loch/src/modules/sunrise-sunset/sunrise-sunset.service.ts similarity index 100% rename from apps/loch/src/modules/sunrise-sunset/sunrise-sunset.service.ts rename to legacy/apps/loch/src/modules/sunrise-sunset/sunrise-sunset.service.ts diff --git a/apps/loch/src/modules/telegram/telegram.module.ts b/legacy/apps/loch/src/modules/telegram/telegram.module.ts similarity index 100% rename from apps/loch/src/modules/telegram/telegram.module.ts rename to legacy/apps/loch/src/modules/telegram/telegram.module.ts diff --git a/apps/loch/src/modules/telegram/telegram.service.ts b/legacy/apps/loch/src/modules/telegram/telegram.service.ts similarity index 100% rename from apps/loch/src/modules/telegram/telegram.service.ts rename to legacy/apps/loch/src/modules/telegram/telegram.service.ts diff --git a/apps/loch/src/modules/weather/weather.module.ts b/legacy/apps/loch/src/modules/weather/weather.module.ts similarity index 100% rename from apps/loch/src/modules/weather/weather.module.ts rename to legacy/apps/loch/src/modules/weather/weather.module.ts diff --git a/apps/loch/src/modules/weather/weather.service.ts b/legacy/apps/loch/src/modules/weather/weather.service.ts similarity index 100% rename from apps/loch/src/modules/weather/weather.service.ts rename to legacy/apps/loch/src/modules/weather/weather.service.ts diff --git a/apps/loch/src/queries/index.ts b/legacy/apps/loch/src/queries/index.ts similarity index 100% rename from apps/loch/src/queries/index.ts rename to legacy/apps/loch/src/queries/index.ts diff --git a/apps/loch/src/queries/sql/supabase/lost-revenue/find-issue-stats-by-status.sql b/legacy/apps/loch/src/queries/sql/supabase/lost-revenue/find-issue-stats-by-status.sql similarity index 100% rename from apps/loch/src/queries/sql/supabase/lost-revenue/find-issue-stats-by-status.sql rename to legacy/apps/loch/src/queries/sql/supabase/lost-revenue/find-issue-stats-by-status.sql diff --git a/apps/loch/src/queries/sql/supabase/lost-revenue/find-issue-stats-by-status.types.ts b/legacy/apps/loch/src/queries/sql/supabase/lost-revenue/find-issue-stats-by-status.types.ts similarity index 100% rename from apps/loch/src/queries/sql/supabase/lost-revenue/find-issue-stats-by-status.types.ts rename to legacy/apps/loch/src/queries/sql/supabase/lost-revenue/find-issue-stats-by-status.types.ts diff --git a/apps/loch/src/queries/sql/supabase/notifications/lock-next-page.sql b/legacy/apps/loch/src/queries/sql/supabase/notifications/lock-next-page.sql similarity index 100% rename from apps/loch/src/queries/sql/supabase/notifications/lock-next-page.sql rename to legacy/apps/loch/src/queries/sql/supabase/notifications/lock-next-page.sql diff --git a/apps/loch/src/queries/sql/supabase/notifications/lock-next-page.types.ts b/legacy/apps/loch/src/queries/sql/supabase/notifications/lock-next-page.types.ts similarity index 100% rename from apps/loch/src/queries/sql/supabase/notifications/lock-next-page.types.ts rename to legacy/apps/loch/src/queries/sql/supabase/notifications/lock-next-page.types.ts diff --git a/apps/loch/src/queries/sql/timescale/lifeline/find-lifeline-connections.sql b/legacy/apps/loch/src/queries/sql/timescale/lifeline/find-lifeline-connections.sql similarity index 100% rename from apps/loch/src/queries/sql/timescale/lifeline/find-lifeline-connections.sql rename to legacy/apps/loch/src/queries/sql/timescale/lifeline/find-lifeline-connections.sql diff --git a/apps/loch/src/queries/sql/timescale/lifeline/find-lifeline-connections.types.ts b/legacy/apps/loch/src/queries/sql/timescale/lifeline/find-lifeline-connections.types.ts similarity index 100% rename from apps/loch/src/queries/sql/timescale/lifeline/find-lifeline-connections.types.ts rename to legacy/apps/loch/src/queries/sql/timescale/lifeline/find-lifeline-connections.types.ts diff --git a/apps/loch/src/queries/sql/timescale/lost-revenue/find-avg-consumption-by-grid-and-type.sql b/legacy/apps/loch/src/queries/sql/timescale/lost-revenue/find-avg-consumption-by-grid-and-type.sql similarity index 100% rename from apps/loch/src/queries/sql/timescale/lost-revenue/find-avg-consumption-by-grid-and-type.sql rename to legacy/apps/loch/src/queries/sql/timescale/lost-revenue/find-avg-consumption-by-grid-and-type.sql diff --git a/apps/loch/src/queries/sql/timescale/lost-revenue/find-avg-consumption-by-grid-and-type.types.ts b/legacy/apps/loch/src/queries/sql/timescale/lost-revenue/find-avg-consumption-by-grid-and-type.types.ts similarity index 100% rename from apps/loch/src/queries/sql/timescale/lost-revenue/find-avg-consumption-by-grid-and-type.types.ts rename to legacy/apps/loch/src/queries/sql/timescale/lost-revenue/find-avg-consumption-by-grid-and-type.types.ts diff --git a/apps/loch/tsconfig.app.json b/legacy/apps/loch/tsconfig.app.json similarity index 100% rename from apps/loch/tsconfig.app.json rename to legacy/apps/loch/tsconfig.app.json diff --git a/apps/loch/tsconfig.json b/legacy/apps/loch/tsconfig.json similarity index 100% rename from apps/loch/tsconfig.json rename to legacy/apps/loch/tsconfig.json diff --git a/apps/loch/tsconfig.spec.json b/legacy/apps/loch/tsconfig.spec.json similarity index 100% rename from apps/loch/tsconfig.spec.json rename to legacy/apps/loch/tsconfig.spec.json diff --git a/apps/loch/webpack.config.js b/legacy/apps/loch/webpack.config.js similarity index 100% rename from apps/loch/webpack.config.js rename to legacy/apps/loch/webpack.config.js diff --git a/apps/talos/.dockerignore b/legacy/apps/talos/.dockerignore similarity index 100% rename from apps/talos/.dockerignore rename to legacy/apps/talos/.dockerignore diff --git a/apps/talos/.env.example b/legacy/apps/talos/.env.example similarity index 100% rename from apps/talos/.env.example rename to legacy/apps/talos/.env.example diff --git a/apps/talos/Dockerfile b/legacy/apps/talos/Dockerfile similarity index 100% rename from apps/talos/Dockerfile rename to legacy/apps/talos/Dockerfile diff --git a/apps/talos/README.md b/legacy/apps/talos/README.md similarity index 100% rename from apps/talos/README.md rename to legacy/apps/talos/README.md diff --git a/apps/talos/jest.config.ts b/legacy/apps/talos/jest.config.ts similarity index 100% rename from apps/talos/jest.config.ts rename to legacy/apps/talos/jest.config.ts diff --git a/apps/talos/project.json b/legacy/apps/talos/project.json similarity index 100% rename from apps/talos/project.json rename to legacy/apps/talos/project.json diff --git a/apps/talos/src/helpers/puppeteer-helpers.ts b/legacy/apps/talos/src/helpers/puppeteer-helpers.ts similarity index 100% rename from apps/talos/src/helpers/puppeteer-helpers.ts rename to legacy/apps/talos/src/helpers/puppeteer-helpers.ts diff --git a/apps/talos/src/main.ts b/legacy/apps/talos/src/main.ts similarity index 100% rename from apps/talos/src/main.ts rename to legacy/apps/talos/src/main.ts diff --git a/apps/talos/src/modules/app.module.ts b/legacy/apps/talos/src/modules/app.module.ts similarity index 100% rename from apps/talos/src/modules/app.module.ts rename to legacy/apps/talos/src/modules/app.module.ts diff --git a/apps/talos/src/modules/calin-v1/calin-v1-core.service.ts b/legacy/apps/talos/src/modules/calin-v1/calin-v1-core.service.ts similarity index 100% rename from apps/talos/src/modules/calin-v1/calin-v1-core.service.ts rename to legacy/apps/talos/src/modules/calin-v1/calin-v1-core.service.ts diff --git a/apps/talos/src/modules/calin-v1/calin-v1-deregistration.service.ts b/legacy/apps/talos/src/modules/calin-v1/calin-v1-deregistration.service.ts similarity index 100% rename from apps/talos/src/modules/calin-v1/calin-v1-deregistration.service.ts rename to legacy/apps/talos/src/modules/calin-v1/calin-v1-deregistration.service.ts diff --git a/apps/talos/src/modules/calin-v1/calin-v1-registration.service.ts b/legacy/apps/talos/src/modules/calin-v1/calin-v1-registration.service.ts similarity index 100% rename from apps/talos/src/modules/calin-v1/calin-v1-registration.service.ts rename to legacy/apps/talos/src/modules/calin-v1/calin-v1-registration.service.ts diff --git a/apps/talos/src/modules/calin-v1/calin-v1.constants.ts b/legacy/apps/talos/src/modules/calin-v1/calin-v1.constants.ts similarity index 100% rename from apps/talos/src/modules/calin-v1/calin-v1.constants.ts rename to legacy/apps/talos/src/modules/calin-v1/calin-v1.constants.ts diff --git a/apps/talos/src/modules/calin-v1/calin-v1.controller.ts b/legacy/apps/talos/src/modules/calin-v1/calin-v1.controller.ts similarity index 100% rename from apps/talos/src/modules/calin-v1/calin-v1.controller.ts rename to legacy/apps/talos/src/modules/calin-v1/calin-v1.controller.ts diff --git a/apps/talos/src/modules/calin-v1/calin-v1.module.ts b/legacy/apps/talos/src/modules/calin-v1/calin-v1.module.ts similarity index 100% rename from apps/talos/src/modules/calin-v1/calin-v1.module.ts rename to legacy/apps/talos/src/modules/calin-v1/calin-v1.module.ts diff --git a/apps/talos/src/modules/calin-v1/dto/register-calin-v1-meter.dto.ts b/legacy/apps/talos/src/modules/calin-v1/dto/register-calin-v1-meter.dto.ts similarity index 100% rename from apps/talos/src/modules/calin-v1/dto/register-calin-v1-meter.dto.ts rename to legacy/apps/talos/src/modules/calin-v1/dto/register-calin-v1-meter.dto.ts diff --git a/apps/talos/src/modules/calin/calin.module.ts b/legacy/apps/talos/src/modules/calin/calin.module.ts similarity index 100% rename from apps/talos/src/modules/calin/calin.module.ts rename to legacy/apps/talos/src/modules/calin/calin.module.ts diff --git a/apps/talos/src/modules/calin/calin.service.ts b/legacy/apps/talos/src/modules/calin/calin.service.ts similarity index 100% rename from apps/talos/src/modules/calin/calin.service.ts rename to legacy/apps/talos/src/modules/calin/calin.service.ts diff --git a/apps/talos/src/modules/calin/dto/dcu-for-install.dto.ts b/legacy/apps/talos/src/modules/calin/dto/dcu-for-install.dto.ts similarity index 100% rename from apps/talos/src/modules/calin/dto/dcu-for-install.dto.ts rename to legacy/apps/talos/src/modules/calin/dto/dcu-for-install.dto.ts diff --git a/apps/talos/src/modules/calin/implementations/calinv1.service.ts b/legacy/apps/talos/src/modules/calin/implementations/calinv1.service.ts similarity index 100% rename from apps/talos/src/modules/calin/implementations/calinv1.service.ts rename to legacy/apps/talos/src/modules/calin/implementations/calinv1.service.ts diff --git a/apps/talos/src/modules/calin/implementations/calinv2.service.ts b/legacy/apps/talos/src/modules/calin/implementations/calinv2.service.ts similarity index 100% rename from apps/talos/src/modules/calin/implementations/calinv2.service.ts rename to legacy/apps/talos/src/modules/calin/implementations/calinv2.service.ts diff --git a/apps/talos/src/modules/debug/debug.controller.ts b/legacy/apps/talos/src/modules/debug/debug.controller.ts similarity index 100% rename from apps/talos/src/modules/debug/debug.controller.ts rename to legacy/apps/talos/src/modules/debug/debug.controller.ts diff --git a/apps/talos/src/modules/metering-hardware-imports/metering-hardware-imports.controller.ts b/legacy/apps/talos/src/modules/metering-hardware-imports/metering-hardware-imports.controller.ts similarity index 100% rename from apps/talos/src/modules/metering-hardware-imports/metering-hardware-imports.controller.ts rename to legacy/apps/talos/src/modules/metering-hardware-imports/metering-hardware-imports.controller.ts diff --git a/apps/talos/src/modules/metering-hardware-imports/metering-hardware-imports.module.ts b/legacy/apps/talos/src/modules/metering-hardware-imports/metering-hardware-imports.module.ts similarity index 100% rename from apps/talos/src/modules/metering-hardware-imports/metering-hardware-imports.module.ts rename to legacy/apps/talos/src/modules/metering-hardware-imports/metering-hardware-imports.module.ts diff --git a/apps/talos/src/modules/metering-hardware-imports/metering-hardware-imports.service.ts b/legacy/apps/talos/src/modules/metering-hardware-imports/metering-hardware-imports.service.ts similarity index 100% rename from apps/talos/src/modules/metering-hardware-imports/metering-hardware-imports.service.ts rename to legacy/apps/talos/src/modules/metering-hardware-imports/metering-hardware-imports.service.ts diff --git a/apps/talos/src/queries/index.ts b/legacy/apps/talos/src/queries/index.ts similarity index 100% rename from apps/talos/src/queries/index.ts rename to legacy/apps/talos/src/queries/index.ts diff --git a/apps/talos/src/queries/sql/supabase/metering-hardware-imports/lock-dcu.sql b/legacy/apps/talos/src/queries/sql/supabase/metering-hardware-imports/lock-dcu.sql similarity index 100% rename from apps/talos/src/queries/sql/supabase/metering-hardware-imports/lock-dcu.sql rename to legacy/apps/talos/src/queries/sql/supabase/metering-hardware-imports/lock-dcu.sql diff --git a/apps/talos/src/queries/sql/supabase/metering-hardware-imports/lock-dcu.types.ts b/legacy/apps/talos/src/queries/sql/supabase/metering-hardware-imports/lock-dcu.types.ts similarity index 100% rename from apps/talos/src/queries/sql/supabase/metering-hardware-imports/lock-dcu.types.ts rename to legacy/apps/talos/src/queries/sql/supabase/metering-hardware-imports/lock-dcu.types.ts diff --git a/apps/talos/src/queries/sql/supabase/metering-hardware-imports/lock-meter.sql b/legacy/apps/talos/src/queries/sql/supabase/metering-hardware-imports/lock-meter.sql similarity index 100% rename from apps/talos/src/queries/sql/supabase/metering-hardware-imports/lock-meter.sql rename to legacy/apps/talos/src/queries/sql/supabase/metering-hardware-imports/lock-meter.sql diff --git a/apps/talos/src/queries/sql/supabase/metering-hardware-imports/lock-meter.types.ts b/legacy/apps/talos/src/queries/sql/supabase/metering-hardware-imports/lock-meter.types.ts similarity index 100% rename from apps/talos/src/queries/sql/supabase/metering-hardware-imports/lock-meter.types.ts rename to legacy/apps/talos/src/queries/sql/supabase/metering-hardware-imports/lock-meter.types.ts diff --git a/apps/talos/tsconfig.app.json b/legacy/apps/talos/tsconfig.app.json similarity index 100% rename from apps/talos/tsconfig.app.json rename to legacy/apps/talos/tsconfig.app.json diff --git a/apps/talos/tsconfig.json b/legacy/apps/talos/tsconfig.json similarity index 100% rename from apps/talos/tsconfig.json rename to legacy/apps/talos/tsconfig.json diff --git a/apps/talos/tsconfig.spec.json b/legacy/apps/talos/tsconfig.spec.json similarity index 100% rename from apps/talos/tsconfig.spec.json rename to legacy/apps/talos/tsconfig.spec.json diff --git a/apps/talos/webpack.config.js b/legacy/apps/talos/webpack.config.js similarity index 100% rename from apps/talos/webpack.config.js rename to legacy/apps/talos/webpack.config.js diff --git a/apps/tiamat/.env.example b/legacy/apps/tiamat/.env.example similarity index 100% rename from apps/tiamat/.env.example rename to legacy/apps/tiamat/.env.example diff --git a/apps/tiamat/README.md b/legacy/apps/tiamat/README.md similarity index 100% rename from apps/tiamat/README.md rename to legacy/apps/tiamat/README.md diff --git a/apps/tiamat/jest.config.ts b/legacy/apps/tiamat/jest.config.ts similarity index 100% rename from apps/tiamat/jest.config.ts rename to legacy/apps/tiamat/jest.config.ts diff --git a/apps/tiamat/project.json b/legacy/apps/tiamat/project.json similarity index 100% rename from apps/tiamat/project.json rename to legacy/apps/tiamat/project.json diff --git a/apps/tiamat/src/main.ts b/legacy/apps/tiamat/src/main.ts similarity index 100% rename from apps/tiamat/src/main.ts rename to legacy/apps/tiamat/src/main.ts diff --git a/apps/tiamat/src/modules/africastalking/africastalking.controller.ts b/legacy/apps/tiamat/src/modules/africastalking/africastalking.controller.ts similarity index 100% rename from apps/tiamat/src/modules/africastalking/africastalking.controller.ts rename to legacy/apps/tiamat/src/modules/africastalking/africastalking.controller.ts diff --git a/apps/tiamat/src/modules/africastalking/africastalking.module.ts b/legacy/apps/tiamat/src/modules/africastalking/africastalking.module.ts similarity index 100% rename from apps/tiamat/src/modules/africastalking/africastalking.module.ts rename to legacy/apps/tiamat/src/modules/africastalking/africastalking.module.ts diff --git a/apps/tiamat/src/modules/agents/agents.controller.ts b/legacy/apps/tiamat/src/modules/agents/agents.controller.ts similarity index 100% rename from apps/tiamat/src/modules/agents/agents.controller.ts rename to legacy/apps/tiamat/src/modules/agents/agents.controller.ts diff --git a/apps/tiamat/src/modules/agents/agents.module.ts b/legacy/apps/tiamat/src/modules/agents/agents.module.ts similarity index 100% rename from apps/tiamat/src/modules/agents/agents.module.ts rename to legacy/apps/tiamat/src/modules/agents/agents.module.ts diff --git a/apps/tiamat/src/modules/app.module.ts b/legacy/apps/tiamat/src/modules/app.module.ts similarity index 86% rename from apps/tiamat/src/modules/app.module.ts rename to legacy/apps/tiamat/src/modules/app.module.ts index 46ee571..9bab17f 100644 --- a/apps/tiamat/src/modules/app.module.ts +++ b/legacy/apps/tiamat/src/modules/app.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { ScheduleModule } from '@nestjs/schedule'; -import { CoreTypeOrmModule, GlobalHttpModule, CoreLoggerModule, GlobalSupabaseModule, CorePgModule } from '@core'; +import { CoreTypeOrmModule, CorePgModule } from '@core'; import { TimeseriesTypeOrmModule } from '@timeseries'; // Core modules @@ -10,11 +10,10 @@ import { CoreEnergyTrackingModule } from '@core/modules/energy-tracking/energy-t import { SoftwareDevAlertModule } from '@core/modules/software-dev-alert/software-dev-alert.module'; // Local modules -import { UserAdminModule } from './user-admin/user-admin.module'; -import { AccountsModule } from './accounts/accounts.module'; +// 002d Task 11: Foundation Nest modules deleted from legacy (auth, user-admin, accounts, +// organizations, api-keys, supabase/http/logger). GridsModule retained (service only; controller gone). import { AfricastalkingModule } from './africastalking/africastalking.module'; import { AgentsModule } from './agents/agents.module'; -import { AuthModule } from './auth/auth.module'; import { ConnectionsModule } from './connections/connections.module'; import { DcusModule } from './dcus/dcus.module'; import { FlutterwaveModule } from './flutterwave/flutterwave.module'; @@ -22,7 +21,6 @@ import { GridsModule } from './grids/grids.module'; import { MetersModule } from './meters/meters.module'; import { MpptsModule } from './mppts/mppts.module'; import { OrdersModule } from './orders/orders.module'; -import { OrganizationsModule } from './organizations/organizations.module'; import { DirectiveBatchesModule } from './directive-batches/directive-batches.module'; import { DirectiveBatchExecutionsModule } from './directive-batch-executions/directive-batch-executions.module'; import { UssdSessionsModule } from './ussd-sessions/ussd-sessions.module'; @@ -31,7 +29,6 @@ import { WebsocketModule } from './websocket/websocket.module'; import { DownloadModule } from './download/download.module'; import { CoreVictronModule } from '@core/modules/victron/victron.module'; import { MeteringHardwareInstallSessionsModule } from './metering-hardware-install-sessions/metering-hardware-install-sessions.module'; -import { ApiKeysModule } from './api-keys/api-keys.module'; import { TelegramModule } from './telegram/telegram.module'; import { IssuesModule } from './issues/issues.module'; import { EpicollectModule } from './epicollect/epicollect.module'; @@ -53,10 +50,7 @@ const modules = process.env.IS_HIBERNATED === 'true' ? [] : [ ScheduleModule.forRoot(), CoreTypeOrmModule, TimeseriesTypeOrmModule, - GlobalHttpModule, - GlobalSupabaseModule, CorePgModule, - CoreLoggerModule, CoreEnergyTrackingModule, CoreSpendingModule, @@ -65,14 +59,10 @@ const modules = process.env.IS_HIBERNATED === 'true' ? [] : [ MeterInteractionsModule, MeterInstallsModule, DataAnalyticsModule, - UserAdminModule, - AccountsModule, AgentsModule, - AuthModule, DcusModule, CoreVictronModule, MeteringHardwareInstallSessionsModule, - OrganizationsModule, FlutterwaveModule, GridsModule, MetersModule, @@ -87,7 +77,6 @@ const modules = process.env.IS_HIBERNATED === 'true' ? [] : [ MpptsModule, WebsocketModule, IssuesModule, - ApiKeysModule, TelegramModule, EpicollectModule, JiraModule, diff --git a/apps/tiamat/src/modules/autopilot/autopilot-fallback-code.ts b/legacy/apps/tiamat/src/modules/autopilot/autopilot-fallback-code.ts similarity index 100% rename from apps/tiamat/src/modules/autopilot/autopilot-fallback-code.ts rename to legacy/apps/tiamat/src/modules/autopilot/autopilot-fallback-code.ts diff --git a/apps/tiamat/src/modules/autopilot/autopilot.controller.ts b/legacy/apps/tiamat/src/modules/autopilot/autopilot.controller.ts similarity index 100% rename from apps/tiamat/src/modules/autopilot/autopilot.controller.ts rename to legacy/apps/tiamat/src/modules/autopilot/autopilot.controller.ts diff --git a/apps/tiamat/src/modules/autopilot/autopilot.module.ts b/legacy/apps/tiamat/src/modules/autopilot/autopilot.module.ts similarity index 100% rename from apps/tiamat/src/modules/autopilot/autopilot.module.ts rename to legacy/apps/tiamat/src/modules/autopilot/autopilot.module.ts diff --git a/apps/tiamat/src/modules/autopilot/autopilot.service.ts b/legacy/apps/tiamat/src/modules/autopilot/autopilot.service.ts similarity index 100% rename from apps/tiamat/src/modules/autopilot/autopilot.service.ts rename to legacy/apps/tiamat/src/modules/autopilot/autopilot.service.ts diff --git a/apps/tiamat/src/modules/chirpstack/chirpstack.controller.ts b/legacy/apps/tiamat/src/modules/chirpstack/chirpstack.controller.ts similarity index 67% rename from apps/tiamat/src/modules/chirpstack/chirpstack.controller.ts rename to legacy/apps/tiamat/src/modules/chirpstack/chirpstack.controller.ts index 42b0fb6..0bc555c 100644 --- a/apps/tiamat/src/modules/chirpstack/chirpstack.controller.ts +++ b/legacy/apps/tiamat/src/modules/chirpstack/chirpstack.controller.ts @@ -17,20 +17,4 @@ export class ChirpstackController { calinIncoming(@Body() body: LorawanCalinEvent) { return this.deviceMessageIncomingService.handle(body, 'CALIN_LORAWAN'); } - - // Receives data from Acrel meters in ChirpStack. - @Post('acrel') - acrel( - @Body() body: any, - ) { - return this.chirpstackService.processIncomingLorawanMessageForAcrelProfile(body); - } - - // Receives data from Acrel meters in ChirpStack. - @Post('milesight') - milesight( - @Body() body: any, - ) { - return this.chirpstackService.processIncomingLorawanMessageForMilesightProfile(body); - } } diff --git a/apps/tiamat/src/modules/chirpstack/chirpstack.module.ts b/legacy/apps/tiamat/src/modules/chirpstack/chirpstack.module.ts similarity index 67% rename from apps/tiamat/src/modules/chirpstack/chirpstack.module.ts rename to legacy/apps/tiamat/src/modules/chirpstack/chirpstack.module.ts index 1357b1b..d850831 100644 --- a/apps/tiamat/src/modules/chirpstack/chirpstack.module.ts +++ b/legacy/apps/tiamat/src/modules/chirpstack/chirpstack.module.ts @@ -1,9 +1,7 @@ import { Module } from '@nestjs/common'; import { ChirpstackController } from './chirpstack.controller'; -import { ChirpStackService } from './chirpstack.service'; @Module({ controllers: [ ChirpstackController ], - providers: [ ChirpStackService ], }) export class ChirpstackModule {} diff --git a/apps/tiamat/src/modules/connections/connections.controller.ts b/legacy/apps/tiamat/src/modules/connections/connections.controller.ts similarity index 100% rename from apps/tiamat/src/modules/connections/connections.controller.ts rename to legacy/apps/tiamat/src/modules/connections/connections.controller.ts diff --git a/apps/tiamat/src/modules/connections/connections.module.ts b/legacy/apps/tiamat/src/modules/connections/connections.module.ts similarity index 100% rename from apps/tiamat/src/modules/connections/connections.module.ts rename to legacy/apps/tiamat/src/modules/connections/connections.module.ts diff --git a/apps/tiamat/src/modules/connections/connections.service.ts b/legacy/apps/tiamat/src/modules/connections/connections.service.ts similarity index 100% rename from apps/tiamat/src/modules/connections/connections.service.ts rename to legacy/apps/tiamat/src/modules/connections/connections.service.ts diff --git a/apps/tiamat/src/modules/connections/dto/update-connection-requested-meters.dto.ts b/legacy/apps/tiamat/src/modules/connections/dto/update-connection-requested-meters.dto.ts similarity index 100% rename from apps/tiamat/src/modules/connections/dto/update-connection-requested-meters.dto.ts rename to legacy/apps/tiamat/src/modules/connections/dto/update-connection-requested-meters.dto.ts diff --git a/apps/tiamat/src/modules/data-analytics/data-analytics.controller.ts b/legacy/apps/tiamat/src/modules/data-analytics/data-analytics.controller.ts similarity index 100% rename from apps/tiamat/src/modules/data-analytics/data-analytics.controller.ts rename to legacy/apps/tiamat/src/modules/data-analytics/data-analytics.controller.ts diff --git a/apps/tiamat/src/modules/data-analytics/data-analytics.module.ts b/legacy/apps/tiamat/src/modules/data-analytics/data-analytics.module.ts similarity index 100% rename from apps/tiamat/src/modules/data-analytics/data-analytics.module.ts rename to legacy/apps/tiamat/src/modules/data-analytics/data-analytics.module.ts diff --git a/apps/tiamat/src/modules/data-analytics/data-analytics.service.ts b/legacy/apps/tiamat/src/modules/data-analytics/data-analytics.service.ts similarity index 100% rename from apps/tiamat/src/modules/data-analytics/data-analytics.service.ts rename to legacy/apps/tiamat/src/modules/data-analytics/data-analytics.service.ts diff --git a/apps/tiamat/src/modules/dcus/dcus.controller.ts b/legacy/apps/tiamat/src/modules/dcus/dcus.controller.ts similarity index 100% rename from apps/tiamat/src/modules/dcus/dcus.controller.ts rename to legacy/apps/tiamat/src/modules/dcus/dcus.controller.ts diff --git a/apps/tiamat/src/modules/dcus/dcus.module.ts b/legacy/apps/tiamat/src/modules/dcus/dcus.module.ts similarity index 100% rename from apps/tiamat/src/modules/dcus/dcus.module.ts rename to legacy/apps/tiamat/src/modules/dcus/dcus.module.ts diff --git a/apps/tiamat/src/modules/dcus/dcus.service.ts b/legacy/apps/tiamat/src/modules/dcus/dcus.service.ts similarity index 100% rename from apps/tiamat/src/modules/dcus/dcus.service.ts rename to legacy/apps/tiamat/src/modules/dcus/dcus.service.ts diff --git a/apps/tiamat/src/modules/dcus/dto/create-dcu-from-inventory.ts b/legacy/apps/tiamat/src/modules/dcus/dto/create-dcu-from-inventory.ts similarity index 100% rename from apps/tiamat/src/modules/dcus/dto/create-dcu-from-inventory.ts rename to legacy/apps/tiamat/src/modules/dcus/dto/create-dcu-from-inventory.ts diff --git a/apps/tiamat/src/modules/device-messages/adapters/calin-api-v1/_incoming.service.ts b/legacy/apps/tiamat/src/modules/device-messages/adapters/calin-api-v1/_incoming.service.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/adapters/calin-api-v1/_incoming.service.ts rename to legacy/apps/tiamat/src/modules/device-messages/adapters/calin-api-v1/_incoming.service.ts diff --git a/apps/tiamat/src/modules/device-messages/adapters/calin-api-v1/_outgoing.service.ts b/legacy/apps/tiamat/src/modules/device-messages/adapters/calin-api-v1/_outgoing.service.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/adapters/calin-api-v1/_outgoing.service.ts rename to legacy/apps/tiamat/src/modules/device-messages/adapters/calin-api-v1/_outgoing.service.ts diff --git a/apps/tiamat/src/modules/device-messages/adapters/calin-api-v1/_token.service.ts b/legacy/apps/tiamat/src/modules/device-messages/adapters/calin-api-v1/_token.service.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/adapters/calin-api-v1/_token.service.ts rename to legacy/apps/tiamat/src/modules/device-messages/adapters/calin-api-v1/_token.service.ts diff --git a/apps/tiamat/src/modules/device-messages/adapters/calin-api-v1/lib/repo.ts b/legacy/apps/tiamat/src/modules/device-messages/adapters/calin-api-v1/lib/repo.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/adapters/calin-api-v1/lib/repo.ts rename to legacy/apps/tiamat/src/modules/device-messages/adapters/calin-api-v1/lib/repo.ts diff --git a/apps/tiamat/src/modules/device-messages/adapters/calin-api-v2/_incoming.service.ts b/legacy/apps/tiamat/src/modules/device-messages/adapters/calin-api-v2/_incoming.service.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/adapters/calin-api-v2/_incoming.service.ts rename to legacy/apps/tiamat/src/modules/device-messages/adapters/calin-api-v2/_incoming.service.ts diff --git a/apps/tiamat/src/modules/device-messages/adapters/calin-api-v2/_outgoing.service.ts b/legacy/apps/tiamat/src/modules/device-messages/adapters/calin-api-v2/_outgoing.service.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/adapters/calin-api-v2/_outgoing.service.ts rename to legacy/apps/tiamat/src/modules/device-messages/adapters/calin-api-v2/_outgoing.service.ts diff --git a/apps/tiamat/src/modules/device-messages/adapters/calin-api-v2/_token.service.ts b/legacy/apps/tiamat/src/modules/device-messages/adapters/calin-api-v2/_token.service.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/adapters/calin-api-v2/_token.service.ts rename to legacy/apps/tiamat/src/modules/device-messages/adapters/calin-api-v2/_token.service.ts diff --git a/apps/tiamat/src/modules/device-messages/adapters/calin-api-v2/lib/repo.ts b/legacy/apps/tiamat/src/modules/device-messages/adapters/calin-api-v2/lib/repo.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/adapters/calin-api-v2/lib/repo.ts rename to legacy/apps/tiamat/src/modules/device-messages/adapters/calin-api-v2/lib/repo.ts diff --git a/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/_incoming.service.ts b/legacy/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/_incoming.service.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/_incoming.service.ts rename to legacy/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/_incoming.service.ts diff --git a/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/_outgoing.service.ts b/legacy/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/_outgoing.service.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/_outgoing.service.ts rename to legacy/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/_outgoing.service.ts diff --git a/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/_UNUSED_EXAMPLE_correlate-request-response.redis.ts b/legacy/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/_UNUSED_EXAMPLE_correlate-request-response.redis.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/_UNUSED_EXAMPLE_correlate-request-response.redis.ts rename to legacy/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/_UNUSED_EXAMPLE_correlate-request-response.redis.ts diff --git a/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/connectivity-helpers.ts b/legacy/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/connectivity-helpers.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/connectivity-helpers.ts rename to legacy/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/connectivity-helpers.ts diff --git a/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/correlate-request-response.ts b/legacy/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/correlate-request-response.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/correlate-request-response.ts rename to legacy/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/correlate-request-response.ts diff --git a/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/decode-response-data.ts b/legacy/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/decode-response-data.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/decode-response-data.ts rename to legacy/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/decode-response-data.ts diff --git a/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/encode-request-data.ts b/legacy/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/encode-request-data.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/encode-request-data.ts rename to legacy/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/encode-request-data.ts diff --git a/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/types.ts b/legacy/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/types.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/types.ts rename to legacy/apps/tiamat/src/modules/device-messages/adapters/calin-lorawan/lib/types.ts diff --git a/apps/tiamat/src/modules/device-messages/adapters/nxt-sts/_token.service.ts b/legacy/apps/tiamat/src/modules/device-messages/adapters/nxt-sts/_token.service.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/adapters/nxt-sts/_token.service.ts rename to legacy/apps/tiamat/src/modules/device-messages/adapters/nxt-sts/_token.service.ts diff --git a/apps/tiamat/src/modules/device-messages/device-messages.module.ts b/legacy/apps/tiamat/src/modules/device-messages/device-messages.module.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/device-messages.module.ts rename to legacy/apps/tiamat/src/modules/device-messages/device-messages.module.ts diff --git a/apps/tiamat/src/modules/device-messages/device-messages.service.ts b/legacy/apps/tiamat/src/modules/device-messages/device-messages.service.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/device-messages.service.ts rename to legacy/apps/tiamat/src/modules/device-messages/device-messages.service.ts diff --git a/apps/tiamat/src/modules/device-messages/dto/create-device-message.dto.ts b/legacy/apps/tiamat/src/modules/device-messages/dto/create-device-message.dto.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/dto/create-device-message.dto.ts rename to legacy/apps/tiamat/src/modules/device-messages/dto/create-device-message.dto.ts diff --git a/apps/tiamat/src/modules/device-messages/dto/generate-token.dto.ts b/legacy/apps/tiamat/src/modules/device-messages/dto/generate-token.dto.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/dto/generate-token.dto.ts rename to legacy/apps/tiamat/src/modules/device-messages/dto/generate-token.dto.ts diff --git a/apps/tiamat/src/modules/device-messages/incoming.service.ts b/legacy/apps/tiamat/src/modules/device-messages/incoming.service.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/incoming.service.ts rename to legacy/apps/tiamat/src/modules/device-messages/incoming.service.ts diff --git a/apps/tiamat/src/modules/device-messages/lib/chirpstack-repository/index.ts b/legacy/apps/tiamat/src/modules/device-messages/lib/chirpstack-repository/index.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/lib/chirpstack-repository/index.ts rename to legacy/apps/tiamat/src/modules/device-messages/lib/chirpstack-repository/index.ts diff --git a/apps/tiamat/src/modules/device-messages/lib/lifecycle.pull.ts b/legacy/apps/tiamat/src/modules/device-messages/lib/lifecycle.pull.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/lib/lifecycle.pull.ts rename to legacy/apps/tiamat/src/modules/device-messages/lib/lifecycle.pull.ts diff --git a/apps/tiamat/src/modules/device-messages/lib/lifecycle.push.ts b/legacy/apps/tiamat/src/modules/device-messages/lib/lifecycle.push.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/lib/lifecycle.push.ts rename to legacy/apps/tiamat/src/modules/device-messages/lib/lifecycle.push.ts diff --git a/apps/tiamat/src/modules/device-messages/lib/queue-moving.pull.ts b/legacy/apps/tiamat/src/modules/device-messages/lib/queue-moving.pull.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/lib/queue-moving.pull.ts rename to legacy/apps/tiamat/src/modules/device-messages/lib/queue-moving.pull.ts diff --git a/apps/tiamat/src/modules/device-messages/lib/queue-moving.push.ts b/legacy/apps/tiamat/src/modules/device-messages/lib/queue-moving.push.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/lib/queue-moving.push.ts rename to legacy/apps/tiamat/src/modules/device-messages/lib/queue-moving.push.ts diff --git a/apps/tiamat/src/modules/device-messages/lib/queue-moving.ts b/legacy/apps/tiamat/src/modules/device-messages/lib/queue-moving.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/lib/queue-moving.ts rename to legacy/apps/tiamat/src/modules/device-messages/lib/queue-moving.ts diff --git a/apps/tiamat/src/modules/device-messages/lib/redis-repository/helpers.ts b/legacy/apps/tiamat/src/modules/device-messages/lib/redis-repository/helpers.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/lib/redis-repository/helpers.ts rename to legacy/apps/tiamat/src/modules/device-messages/lib/redis-repository/helpers.ts diff --git a/apps/tiamat/src/modules/device-messages/lib/redis-repository/index.ts b/legacy/apps/tiamat/src/modules/device-messages/lib/redis-repository/index.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/lib/redis-repository/index.ts rename to legacy/apps/tiamat/src/modules/device-messages/lib/redis-repository/index.ts diff --git a/apps/tiamat/src/modules/device-messages/lib/redis-repository/keys.ts b/legacy/apps/tiamat/src/modules/device-messages/lib/redis-repository/keys.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/lib/redis-repository/keys.ts rename to legacy/apps/tiamat/src/modules/device-messages/lib/redis-repository/keys.ts diff --git a/apps/tiamat/src/modules/device-messages/lib/retry-helpers.ts b/legacy/apps/tiamat/src/modules/device-messages/lib/retry-helpers.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/lib/retry-helpers.ts rename to legacy/apps/tiamat/src/modules/device-messages/lib/retry-helpers.ts diff --git a/apps/tiamat/src/modules/device-messages/lib/types.ts b/legacy/apps/tiamat/src/modules/device-messages/lib/types.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/lib/types.ts rename to legacy/apps/tiamat/src/modules/device-messages/lib/types.ts diff --git a/apps/tiamat/src/modules/device-messages/outgoing.service.ts b/legacy/apps/tiamat/src/modules/device-messages/outgoing.service.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/outgoing.service.ts rename to legacy/apps/tiamat/src/modules/device-messages/outgoing.service.ts diff --git a/apps/tiamat/src/modules/device-messages/token.service.ts b/legacy/apps/tiamat/src/modules/device-messages/token.service.ts similarity index 100% rename from apps/tiamat/src/modules/device-messages/token.service.ts rename to legacy/apps/tiamat/src/modules/device-messages/token.service.ts diff --git a/apps/tiamat/src/modules/directive-batch-executions/directive-batch-executions.controller.ts b/legacy/apps/tiamat/src/modules/directive-batch-executions/directive-batch-executions.controller.ts similarity index 100% rename from apps/tiamat/src/modules/directive-batch-executions/directive-batch-executions.controller.ts rename to legacy/apps/tiamat/src/modules/directive-batch-executions/directive-batch-executions.controller.ts diff --git a/apps/tiamat/src/modules/directive-batch-executions/directive-batch-executions.module.ts b/legacy/apps/tiamat/src/modules/directive-batch-executions/directive-batch-executions.module.ts similarity index 100% rename from apps/tiamat/src/modules/directive-batch-executions/directive-batch-executions.module.ts rename to legacy/apps/tiamat/src/modules/directive-batch-executions/directive-batch-executions.module.ts diff --git a/apps/tiamat/src/modules/directive-batch-executions/directive-batch-executions.service.ts b/legacy/apps/tiamat/src/modules/directive-batch-executions/directive-batch-executions.service.ts similarity index 100% rename from apps/tiamat/src/modules/directive-batch-executions/directive-batch-executions.service.ts rename to legacy/apps/tiamat/src/modules/directive-batch-executions/directive-batch-executions.service.ts diff --git a/apps/tiamat/src/modules/directive-batches/directive-batches.controller.ts b/legacy/apps/tiamat/src/modules/directive-batches/directive-batches.controller.ts similarity index 100% rename from apps/tiamat/src/modules/directive-batches/directive-batches.controller.ts rename to legacy/apps/tiamat/src/modules/directive-batches/directive-batches.controller.ts diff --git a/apps/tiamat/src/modules/directive-batches/directive-batches.module.ts b/legacy/apps/tiamat/src/modules/directive-batches/directive-batches.module.ts similarity index 100% rename from apps/tiamat/src/modules/directive-batches/directive-batches.module.ts rename to legacy/apps/tiamat/src/modules/directive-batches/directive-batches.module.ts diff --git a/apps/tiamat/src/modules/directive-batches/directive-batches.service.ts b/legacy/apps/tiamat/src/modules/directive-batches/directive-batches.service.ts similarity index 100% rename from apps/tiamat/src/modules/directive-batches/directive-batches.service.ts rename to legacy/apps/tiamat/src/modules/directive-batches/directive-batches.service.ts diff --git a/apps/tiamat/src/modules/directive-batches/dto/UpsertDirectiveBatchDto.ts b/legacy/apps/tiamat/src/modules/directive-batches/dto/UpsertDirectiveBatchDto.ts similarity index 83% rename from apps/tiamat/src/modules/directive-batches/dto/UpsertDirectiveBatchDto.ts rename to legacy/apps/tiamat/src/modules/directive-batches/dto/UpsertDirectiveBatchDto.ts index 6e55440..5090a01 100644 --- a/apps/tiamat/src/modules/directive-batches/dto/UpsertDirectiveBatchDto.ts +++ b/legacy/apps/tiamat/src/modules/directive-batches/dto/UpsertDirectiveBatchDto.ts @@ -13,11 +13,6 @@ export class UpsertDirectiveBatchDto { @IsIn(Constants.public.Enums.meter_interaction_type_enum) task_type: MeterInteractionTypeEnum; - // @TODO :: Deprecate - @IsOptional() - @IsIn(Constants.public.Enums.directive_type_enum) - directive_type: DirectiveTypeEnum; - @IsIn(Constants.public.Enums.fs_command_type_enum) fs_command: FCommandTypeEnum; diff --git a/apps/tiamat/src/modules/download/download-helpers.ts b/legacy/apps/tiamat/src/modules/download/download-helpers.ts similarity index 100% rename from apps/tiamat/src/modules/download/download-helpers.ts rename to legacy/apps/tiamat/src/modules/download/download-helpers.ts diff --git a/apps/tiamat/src/modules/download/download.module.ts b/legacy/apps/tiamat/src/modules/download/download.module.ts similarity index 100% rename from apps/tiamat/src/modules/download/download.module.ts rename to legacy/apps/tiamat/src/modules/download/download.module.ts diff --git a/apps/tiamat/src/modules/download/download.service.ts b/legacy/apps/tiamat/src/modules/download/download.service.ts similarity index 100% rename from apps/tiamat/src/modules/download/download.service.ts rename to legacy/apps/tiamat/src/modules/download/download.service.ts diff --git a/apps/tiamat/src/modules/epicollect/epicollect.controller.ts b/legacy/apps/tiamat/src/modules/epicollect/epicollect.controller.ts similarity index 100% rename from apps/tiamat/src/modules/epicollect/epicollect.controller.ts rename to legacy/apps/tiamat/src/modules/epicollect/epicollect.controller.ts diff --git a/apps/tiamat/src/modules/epicollect/epicollect.module.ts b/legacy/apps/tiamat/src/modules/epicollect/epicollect.module.ts similarity index 100% rename from apps/tiamat/src/modules/epicollect/epicollect.module.ts rename to legacy/apps/tiamat/src/modules/epicollect/epicollect.module.ts diff --git a/apps/tiamat/src/modules/epicollect/epicollect.service.ts b/legacy/apps/tiamat/src/modules/epicollect/epicollect.service.ts similarity index 100% rename from apps/tiamat/src/modules/epicollect/epicollect.service.ts rename to legacy/apps/tiamat/src/modules/epicollect/epicollect.service.ts diff --git a/apps/tiamat/src/modules/flutterwave/flutterwave.controller.ts b/legacy/apps/tiamat/src/modules/flutterwave/flutterwave.controller.ts similarity index 100% rename from apps/tiamat/src/modules/flutterwave/flutterwave.controller.ts rename to legacy/apps/tiamat/src/modules/flutterwave/flutterwave.controller.ts diff --git a/apps/tiamat/src/modules/flutterwave/flutterwave.module.ts b/legacy/apps/tiamat/src/modules/flutterwave/flutterwave.module.ts similarity index 100% rename from apps/tiamat/src/modules/flutterwave/flutterwave.module.ts rename to legacy/apps/tiamat/src/modules/flutterwave/flutterwave.module.ts diff --git a/apps/tiamat/src/modules/flutterwave/flutterwave.service.ts b/legacy/apps/tiamat/src/modules/flutterwave/flutterwave.service.ts similarity index 100% rename from apps/tiamat/src/modules/flutterwave/flutterwave.service.ts rename to legacy/apps/tiamat/src/modules/flutterwave/flutterwave.service.ts diff --git a/apps/tiamat/src/modules/grids/grids.controller.ts b/legacy/apps/tiamat/src/modules/grids/grids.controller.ts similarity index 77% rename from apps/tiamat/src/modules/grids/grids.controller.ts rename to legacy/apps/tiamat/src/modules/grids/grids.controller.ts index c36101d..e2590d3 100644 --- a/apps/tiamat/src/modules/grids/grids.controller.ts +++ b/legacy/apps/tiamat/src/modules/grids/grids.controller.ts @@ -2,9 +2,12 @@ import { Body, Controller, Get, Param, Put, StreamableFile, UseGuards } from '@n import { GridsService } from './grids.service'; import { UpdateGridInput } from '@core/modules/grids/dto/update-grid.input'; import { DownloadService } from '../download/download.service'; -// import { CurrentUser, NxtSupabaseUser } from '../auth/nxt-supabase-user'; import { AuthenticationGuard } from '../auth/authentication.guard'; +/** + * 002d Task 11: `GET /grids/:id` (Flow XO) removed — not ported to OSS Foundation. + * Remaining routes stay until their owning capabilities absorb them (Metering / download). + */ @UseGuards(AuthenticationGuard) @Controller('grids') export class GridsController { @@ -14,6 +17,7 @@ export class GridsController { ) {} // Called by Yeti when done updating the DCU status, the MPPT info and the grid's diagnostics + // (loch weather also PUTs here under the legacy api-only ops-DB write funnel — dropped in OSS) @Put() updateGrids(@Body() body: UpdateGridInput[]) { return this.service.updateMany(body); @@ -25,15 +29,9 @@ export class GridsController { return new StreamableFile(file); } - // Used by Google App Script + // Used by Google App Script — re-home to Metering (ADR-013) @Get('/:id/metering_hardware_connectivity_stats') async getConnectivityStatsByGridId(@Param('id') gridId: number): Promise { return this.service.getMeteringHardwareConnectivityStatsByGridId(gridId); } - - // This is used by Flow XO to look for grids - @Get(':id') - getGrid(@Param('id') id) { - return this.service.findOne(id); - } } diff --git a/apps/tiamat/src/modules/grids/grids.module.ts b/legacy/apps/tiamat/src/modules/grids/grids.module.ts similarity index 72% rename from apps/tiamat/src/modules/grids/grids.module.ts rename to legacy/apps/tiamat/src/modules/grids/grids.module.ts index dce373e..9b410a7 100644 --- a/apps/tiamat/src/modules/grids/grids.module.ts +++ b/legacy/apps/tiamat/src/modules/grids/grids.module.ts @@ -4,6 +4,10 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { GridsController } from './grids.controller'; import { Grid } from '@core/modules/grids/entities/grid.entity'; +/** + * 002d Task 11: Nest `GET /grids/:id` removed from controller (not ported). + * Service retained for in-legacy callers + Metering re-home (connectivity / cabin-credit). + */ @Global() @Module({ imports: [ TypeOrmModule.forFeature([ Grid ]) ], diff --git a/apps/tiamat/src/modules/grids/grids.service.ts b/legacy/apps/tiamat/src/modules/grids/grids.service.ts similarity index 89% rename from apps/tiamat/src/modules/grids/grids.service.ts rename to legacy/apps/tiamat/src/modules/grids/grids.service.ts index b888ece..34f6316 100644 --- a/apps/tiamat/src/modules/grids/grids.service.ts +++ b/legacy/apps/tiamat/src/modules/grids/grids.service.ts @@ -19,6 +19,14 @@ import { UpdateGridInput } from '@core/modules/grids/dto/update-grid.input'; import { getConnectivityStats } from './queries/getConnectivityStats.query'; import { SupabaseService } from '@core/modules/supabase.module'; +/** + * 002d Task 10/11 — Nest grids HTTP not ported to OSS Foundation (table/RLS/seed only). + * `GET /grids/:id` removed from controller at Task 11. Retain this service until Metering absorbs: + * - getMeteringHardwareConnectivityStatsByGridId → Metering (pending) + * - recalculateCabinCreditDepletion* → Metering (pending; also unused in-repo) + * Call-site Supabase selects replace shared findOne/update for new code; ops-DB write + * funnel through api is dropped. + */ @Injectable() export class GridsService extends CoreGridsService { constructor( diff --git a/apps/tiamat/src/modules/grids/queries/getConnectivityStats.query.ts b/legacy/apps/tiamat/src/modules/grids/queries/getConnectivityStats.query.ts similarity index 100% rename from apps/tiamat/src/modules/grids/queries/getConnectivityStats.query.ts rename to legacy/apps/tiamat/src/modules/grids/queries/getConnectivityStats.query.ts diff --git a/apps/tiamat/src/modules/issues/constants/issue-config-map.ts b/legacy/apps/tiamat/src/modules/issues/constants/issue-config-map.ts similarity index 100% rename from apps/tiamat/src/modules/issues/constants/issue-config-map.ts rename to legacy/apps/tiamat/src/modules/issues/constants/issue-config-map.ts diff --git a/apps/tiamat/src/modules/issues/issues-new-service.ts b/legacy/apps/tiamat/src/modules/issues/issues-new-service.ts similarity index 100% rename from apps/tiamat/src/modules/issues/issues-new-service.ts rename to legacy/apps/tiamat/src/modules/issues/issues-new-service.ts diff --git a/apps/tiamat/src/modules/issues/issues.controller.ts b/legacy/apps/tiamat/src/modules/issues/issues.controller.ts similarity index 100% rename from apps/tiamat/src/modules/issues/issues.controller.ts rename to legacy/apps/tiamat/src/modules/issues/issues.controller.ts diff --git a/apps/tiamat/src/modules/issues/issues.module.ts b/legacy/apps/tiamat/src/modules/issues/issues.module.ts similarity index 100% rename from apps/tiamat/src/modules/issues/issues.module.ts rename to legacy/apps/tiamat/src/modules/issues/issues.module.ts diff --git a/apps/tiamat/src/modules/issues/issues.service.ts b/legacy/apps/tiamat/src/modules/issues/issues.service.ts similarity index 100% rename from apps/tiamat/src/modules/issues/issues.service.ts rename to legacy/apps/tiamat/src/modules/issues/issues.service.ts diff --git a/apps/tiamat/src/modules/jira/jira.controller.ts b/legacy/apps/tiamat/src/modules/jira/jira.controller.ts similarity index 100% rename from apps/tiamat/src/modules/jira/jira.controller.ts rename to legacy/apps/tiamat/src/modules/jira/jira.controller.ts diff --git a/apps/tiamat/src/modules/jira/jira.module.ts b/legacy/apps/tiamat/src/modules/jira/jira.module.ts similarity index 100% rename from apps/tiamat/src/modules/jira/jira.module.ts rename to legacy/apps/tiamat/src/modules/jira/jira.module.ts diff --git a/apps/tiamat/src/modules/jira/jira.service.ts b/legacy/apps/tiamat/src/modules/jira/jira.service.ts similarity index 100% rename from apps/tiamat/src/modules/jira/jira.service.ts rename to legacy/apps/tiamat/src/modules/jira/jira.service.ts diff --git a/apps/tiamat/src/modules/lost-revenue/lost-revenue.controller.ts b/legacy/apps/tiamat/src/modules/lost-revenue/lost-revenue.controller.ts similarity index 100% rename from apps/tiamat/src/modules/lost-revenue/lost-revenue.controller.ts rename to legacy/apps/tiamat/src/modules/lost-revenue/lost-revenue.controller.ts diff --git a/apps/tiamat/src/modules/lost-revenue/lost-revenue.module.ts b/legacy/apps/tiamat/src/modules/lost-revenue/lost-revenue.module.ts similarity index 100% rename from apps/tiamat/src/modules/lost-revenue/lost-revenue.module.ts rename to legacy/apps/tiamat/src/modules/lost-revenue/lost-revenue.module.ts diff --git a/apps/tiamat/src/modules/lost-revenue/lost-revenue.service.ts b/legacy/apps/tiamat/src/modules/lost-revenue/lost-revenue.service.ts similarity index 100% rename from apps/tiamat/src/modules/lost-revenue/lost-revenue.service.ts rename to legacy/apps/tiamat/src/modules/lost-revenue/lost-revenue.service.ts diff --git a/apps/tiamat/src/modules/lost-revenue/tests/lost-revenue.spec.ts.backup b/legacy/apps/tiamat/src/modules/lost-revenue/tests/lost-revenue.spec.ts.backup similarity index 100% rename from apps/tiamat/src/modules/lost-revenue/tests/lost-revenue.spec.ts.backup rename to legacy/apps/tiamat/src/modules/lost-revenue/tests/lost-revenue.spec.ts.backup diff --git a/apps/tiamat/src/modules/lost-revenue/tests/mockDataGenerator.ts b/legacy/apps/tiamat/src/modules/lost-revenue/tests/mockDataGenerator.ts similarity index 100% rename from apps/tiamat/src/modules/lost-revenue/tests/mockDataGenerator.ts rename to legacy/apps/tiamat/src/modules/lost-revenue/tests/mockDataGenerator.ts diff --git a/apps/tiamat/src/modules/meter-installs/adapters/calin-api-v1/_install.service.ts b/legacy/apps/tiamat/src/modules/meter-installs/adapters/calin-api-v1/_install.service.ts similarity index 100% rename from apps/tiamat/src/modules/meter-installs/adapters/calin-api-v1/_install.service.ts rename to legacy/apps/tiamat/src/modules/meter-installs/adapters/calin-api-v1/_install.service.ts diff --git a/apps/tiamat/src/modules/meter-installs/adapters/calin-api-v2/_install.service.ts b/legacy/apps/tiamat/src/modules/meter-installs/adapters/calin-api-v2/_install.service.ts similarity index 100% rename from apps/tiamat/src/modules/meter-installs/adapters/calin-api-v2/_install.service.ts rename to legacy/apps/tiamat/src/modules/meter-installs/adapters/calin-api-v2/_install.service.ts diff --git a/apps/tiamat/src/modules/meter-installs/adapters/calin-lorawan/_install.service.ts b/legacy/apps/tiamat/src/modules/meter-installs/adapters/calin-lorawan/_install.service.ts similarity index 100% rename from apps/tiamat/src/modules/meter-installs/adapters/calin-lorawan/_install.service.ts rename to legacy/apps/tiamat/src/modules/meter-installs/adapters/calin-lorawan/_install.service.ts diff --git a/apps/tiamat/src/modules/meter-installs/dto/meter-for-ns-registration.dto.ts b/legacy/apps/tiamat/src/modules/meter-installs/dto/meter-for-ns-registration.dto.ts similarity index 100% rename from apps/tiamat/src/modules/meter-installs/dto/meter-for-ns-registration.dto.ts rename to legacy/apps/tiamat/src/modules/meter-installs/dto/meter-for-ns-registration.dto.ts diff --git a/apps/tiamat/src/modules/meter-installs/meter-installs.controller.ts b/legacy/apps/tiamat/src/modules/meter-installs/meter-installs.controller.ts similarity index 100% rename from apps/tiamat/src/modules/meter-installs/meter-installs.controller.ts rename to legacy/apps/tiamat/src/modules/meter-installs/meter-installs.controller.ts diff --git a/apps/tiamat/src/modules/meter-installs/meter-installs.module.ts b/legacy/apps/tiamat/src/modules/meter-installs/meter-installs.module.ts similarity index 100% rename from apps/tiamat/src/modules/meter-installs/meter-installs.module.ts rename to legacy/apps/tiamat/src/modules/meter-installs/meter-installs.module.ts diff --git a/apps/tiamat/src/modules/meter-installs/meter-installs.service.ts b/legacy/apps/tiamat/src/modules/meter-installs/meter-installs.service.ts similarity index 100% rename from apps/tiamat/src/modules/meter-installs/meter-installs.service.ts rename to legacy/apps/tiamat/src/modules/meter-installs/meter-installs.service.ts diff --git a/apps/tiamat/src/modules/meter-installs/meter-uninstalls.service.ts b/legacy/apps/tiamat/src/modules/meter-installs/meter-uninstalls.service.ts similarity index 100% rename from apps/tiamat/src/modules/meter-installs/meter-uninstalls.service.ts rename to legacy/apps/tiamat/src/modules/meter-installs/meter-uninstalls.service.ts diff --git a/apps/tiamat/src/modules/meter-interactions/dto/create-meter-interaction.dto.ts b/legacy/apps/tiamat/src/modules/meter-interactions/dto/create-meter-interaction.dto.ts similarity index 100% rename from apps/tiamat/src/modules/meter-interactions/dto/create-meter-interaction.dto.ts rename to legacy/apps/tiamat/src/modules/meter-interactions/dto/create-meter-interaction.dto.ts diff --git a/apps/tiamat/src/modules/meter-interactions/dto/delivery-status.dto.ts b/legacy/apps/tiamat/src/modules/meter-interactions/dto/delivery-status.dto.ts similarity index 100% rename from apps/tiamat/src/modules/meter-interactions/dto/delivery-status.dto.ts rename to legacy/apps/tiamat/src/modules/meter-interactions/dto/delivery-status.dto.ts diff --git a/apps/tiamat/src/modules/meter-interactions/grid-digital-twin.service.ts b/legacy/apps/tiamat/src/modules/meter-interactions/grid-digital-twin.service.ts similarity index 100% rename from apps/tiamat/src/modules/meter-interactions/grid-digital-twin.service.ts rename to legacy/apps/tiamat/src/modules/meter-interactions/grid-digital-twin.service.ts diff --git a/apps/tiamat/src/modules/meter-interactions/interaction-after-effects.service.ts b/legacy/apps/tiamat/src/modules/meter-interactions/interaction-after-effects.service.ts similarity index 100% rename from apps/tiamat/src/modules/meter-interactions/interaction-after-effects.service.ts rename to legacy/apps/tiamat/src/modules/meter-interactions/interaction-after-effects.service.ts diff --git a/apps/tiamat/src/modules/meter-interactions/interaction-gatekeeper.service.ts b/legacy/apps/tiamat/src/modules/meter-interactions/interaction-gatekeeper.service.ts similarity index 100% rename from apps/tiamat/src/modules/meter-interactions/interaction-gatekeeper.service.ts rename to legacy/apps/tiamat/src/modules/meter-interactions/interaction-gatekeeper.service.ts diff --git a/apps/tiamat/src/modules/meter-interactions/lib/interaction-context.ts b/legacy/apps/tiamat/src/modules/meter-interactions/lib/interaction-context.ts similarity index 100% rename from apps/tiamat/src/modules/meter-interactions/lib/interaction-context.ts rename to legacy/apps/tiamat/src/modules/meter-interactions/lib/interaction-context.ts diff --git a/apps/tiamat/src/modules/meter-interactions/lib/kwh-to-monetary-balance.ts b/legacy/apps/tiamat/src/modules/meter-interactions/lib/kwh-to-monetary-balance.ts similarity index 100% rename from apps/tiamat/src/modules/meter-interactions/lib/kwh-to-monetary-balance.ts rename to legacy/apps/tiamat/src/modules/meter-interactions/lib/kwh-to-monetary-balance.ts diff --git a/apps/tiamat/src/modules/meter-interactions/lib/manufacturer-protocol.ts b/legacy/apps/tiamat/src/modules/meter-interactions/lib/manufacturer-protocol.ts similarity index 100% rename from apps/tiamat/src/modules/meter-interactions/lib/manufacturer-protocol.ts rename to legacy/apps/tiamat/src/modules/meter-interactions/lib/manufacturer-protocol.ts diff --git a/apps/tiamat/src/modules/meter-interactions/lib/meter-interaction-status-helpers.ts b/legacy/apps/tiamat/src/modules/meter-interactions/lib/meter-interaction-status-helpers.ts similarity index 100% rename from apps/tiamat/src/modules/meter-interactions/lib/meter-interaction-status-helpers.ts rename to legacy/apps/tiamat/src/modules/meter-interactions/lib/meter-interaction-status-helpers.ts diff --git a/apps/tiamat/src/modules/meter-interactions/lib/meter-interaction-type-helpers.ts b/legacy/apps/tiamat/src/modules/meter-interactions/lib/meter-interaction-type-helpers.ts similarity index 100% rename from apps/tiamat/src/modules/meter-interactions/lib/meter-interaction-type-helpers.ts rename to legacy/apps/tiamat/src/modules/meter-interactions/lib/meter-interaction-type-helpers.ts diff --git a/apps/tiamat/src/modules/meter-interactions/lib/supabase.ts b/legacy/apps/tiamat/src/modules/meter-interactions/lib/supabase.ts similarity index 100% rename from apps/tiamat/src/modules/meter-interactions/lib/supabase.ts rename to legacy/apps/tiamat/src/modules/meter-interactions/lib/supabase.ts diff --git a/apps/tiamat/src/modules/meter-interactions/lib/three-phase-aggregation.ts b/legacy/apps/tiamat/src/modules/meter-interactions/lib/three-phase-aggregation.ts similarity index 100% rename from apps/tiamat/src/modules/meter-interactions/lib/three-phase-aggregation.ts rename to legacy/apps/tiamat/src/modules/meter-interactions/lib/three-phase-aggregation.ts diff --git a/apps/tiamat/src/modules/meter-interactions/meter-interactions.controller.ts b/legacy/apps/tiamat/src/modules/meter-interactions/meter-interactions.controller.ts similarity index 100% rename from apps/tiamat/src/modules/meter-interactions/meter-interactions.controller.ts rename to legacy/apps/tiamat/src/modules/meter-interactions/meter-interactions.controller.ts diff --git a/apps/tiamat/src/modules/meter-interactions/meter-interactions.module.ts b/legacy/apps/tiamat/src/modules/meter-interactions/meter-interactions.module.ts similarity index 100% rename from apps/tiamat/src/modules/meter-interactions/meter-interactions.module.ts rename to legacy/apps/tiamat/src/modules/meter-interactions/meter-interactions.module.ts diff --git a/apps/tiamat/src/modules/meter-interactions/meter-interactions.service.ts b/legacy/apps/tiamat/src/modules/meter-interactions/meter-interactions.service.ts similarity index 100% rename from apps/tiamat/src/modules/meter-interactions/meter-interactions.service.ts rename to legacy/apps/tiamat/src/modules/meter-interactions/meter-interactions.service.ts diff --git a/apps/tiamat/src/modules/metering-hardware-install-sessions/dto/create-metering-hardware-install-session.input.ts b/legacy/apps/tiamat/src/modules/metering-hardware-install-sessions/dto/create-metering-hardware-install-session.input.ts similarity index 100% rename from apps/tiamat/src/modules/metering-hardware-install-sessions/dto/create-metering-hardware-install-session.input.ts rename to legacy/apps/tiamat/src/modules/metering-hardware-install-sessions/dto/create-metering-hardware-install-session.input.ts diff --git a/apps/tiamat/src/modules/metering-hardware-install-sessions/dto/update-metering-hardware-install-session.input.ts b/legacy/apps/tiamat/src/modules/metering-hardware-install-sessions/dto/update-metering-hardware-install-session.input.ts similarity index 100% rename from apps/tiamat/src/modules/metering-hardware-install-sessions/dto/update-metering-hardware-install-session.input.ts rename to legacy/apps/tiamat/src/modules/metering-hardware-install-sessions/dto/update-metering-hardware-install-session.input.ts diff --git a/apps/tiamat/src/modules/metering-hardware-install-sessions/metering-hardware-install-sessions.module.ts b/legacy/apps/tiamat/src/modules/metering-hardware-install-sessions/metering-hardware-install-sessions.module.ts similarity index 100% rename from apps/tiamat/src/modules/metering-hardware-install-sessions/metering-hardware-install-sessions.module.ts rename to legacy/apps/tiamat/src/modules/metering-hardware-install-sessions/metering-hardware-install-sessions.module.ts diff --git a/apps/tiamat/src/modules/metering-hardware-install-sessions/metering-hardware-install-sessions.service.ts b/legacy/apps/tiamat/src/modules/metering-hardware-install-sessions/metering-hardware-install-sessions.service.ts similarity index 100% rename from apps/tiamat/src/modules/metering-hardware-install-sessions/metering-hardware-install-sessions.service.ts rename to legacy/apps/tiamat/src/modules/metering-hardware-install-sessions/metering-hardware-install-sessions.service.ts diff --git a/apps/tiamat/src/modules/meters/dto/assign-meter.dto.ts b/legacy/apps/tiamat/src/modules/meters/dto/assign-meter.dto.ts similarity index 100% rename from apps/tiamat/src/modules/meters/dto/assign-meter.dto.ts rename to legacy/apps/tiamat/src/modules/meters/dto/assign-meter.dto.ts diff --git a/apps/tiamat/src/modules/meters/dto/create-meter-from-inventory.dto.ts b/legacy/apps/tiamat/src/modules/meters/dto/create-meter-from-inventory.dto.ts similarity index 100% rename from apps/tiamat/src/modules/meters/dto/create-meter-from-inventory.dto.ts rename to legacy/apps/tiamat/src/modules/meters/dto/create-meter-from-inventory.dto.ts diff --git a/apps/tiamat/src/modules/meters/dto/token-delivery.dto.ts b/legacy/apps/tiamat/src/modules/meters/dto/token-delivery.dto.ts similarity index 100% rename from apps/tiamat/src/modules/meters/dto/token-delivery.dto.ts rename to legacy/apps/tiamat/src/modules/meters/dto/token-delivery.dto.ts diff --git a/apps/tiamat/src/modules/meters/dto/token-generation.dto.ts b/legacy/apps/tiamat/src/modules/meters/dto/token-generation.dto.ts similarity index 100% rename from apps/tiamat/src/modules/meters/dto/token-generation.dto.ts rename to legacy/apps/tiamat/src/modules/meters/dto/token-generation.dto.ts diff --git a/apps/tiamat/src/modules/meters/meters.controller.ts b/legacy/apps/tiamat/src/modules/meters/meters.controller.ts similarity index 100% rename from apps/tiamat/src/modules/meters/meters.controller.ts rename to legacy/apps/tiamat/src/modules/meters/meters.controller.ts diff --git a/apps/tiamat/src/modules/meters/meters.module.ts b/legacy/apps/tiamat/src/modules/meters/meters.module.ts similarity index 100% rename from apps/tiamat/src/modules/meters/meters.module.ts rename to legacy/apps/tiamat/src/modules/meters/meters.module.ts diff --git a/apps/tiamat/src/modules/meters/meters.service.ts b/legacy/apps/tiamat/src/modules/meters/meters.service.ts similarity index 100% rename from apps/tiamat/src/modules/meters/meters.service.ts rename to legacy/apps/tiamat/src/modules/meters/meters.service.ts diff --git a/apps/tiamat/src/modules/mppts/mppts.controller.ts b/legacy/apps/tiamat/src/modules/mppts/mppts.controller.ts similarity index 100% rename from apps/tiamat/src/modules/mppts/mppts.controller.ts rename to legacy/apps/tiamat/src/modules/mppts/mppts.controller.ts diff --git a/apps/tiamat/src/modules/mppts/mppts.module.ts b/legacy/apps/tiamat/src/modules/mppts/mppts.module.ts similarity index 100% rename from apps/tiamat/src/modules/mppts/mppts.module.ts rename to legacy/apps/tiamat/src/modules/mppts/mppts.module.ts diff --git a/apps/tiamat/src/modules/notifications/notifications.controller.ts b/legacy/apps/tiamat/src/modules/notifications/notifications.controller.ts similarity index 100% rename from apps/tiamat/src/modules/notifications/notifications.controller.ts rename to legacy/apps/tiamat/src/modules/notifications/notifications.controller.ts diff --git a/apps/tiamat/src/modules/notifications/notifications.module.ts b/legacy/apps/tiamat/src/modules/notifications/notifications.module.ts similarity index 100% rename from apps/tiamat/src/modules/notifications/notifications.module.ts rename to legacy/apps/tiamat/src/modules/notifications/notifications.module.ts diff --git a/apps/tiamat/src/modules/notifications/notifications.service.ts b/legacy/apps/tiamat/src/modules/notifications/notifications.service.ts similarity index 100% rename from apps/tiamat/src/modules/notifications/notifications.service.ts rename to legacy/apps/tiamat/src/modules/notifications/notifications.service.ts diff --git a/apps/tiamat/src/modules/orders/dto/create-order.dto.ts b/legacy/apps/tiamat/src/modules/orders/dto/create-order.dto.ts similarity index 100% rename from apps/tiamat/src/modules/orders/dto/create-order.dto.ts rename to legacy/apps/tiamat/src/modules/orders/dto/create-order.dto.ts diff --git a/apps/tiamat/src/modules/orders/dto/initialise-flutterwave-order.dto.ts b/legacy/apps/tiamat/src/modules/orders/dto/initialise-flutterwave-order.dto.ts similarity index 100% rename from apps/tiamat/src/modules/orders/dto/initialise-flutterwave-order.dto.ts rename to legacy/apps/tiamat/src/modules/orders/dto/initialise-flutterwave-order.dto.ts diff --git a/apps/tiamat/src/modules/orders/dto/verify-flutterwave-order.dto.ts b/legacy/apps/tiamat/src/modules/orders/dto/verify-flutterwave-order.dto.ts similarity index 100% rename from apps/tiamat/src/modules/orders/dto/verify-flutterwave-order.dto.ts rename to legacy/apps/tiamat/src/modules/orders/dto/verify-flutterwave-order.dto.ts diff --git a/apps/tiamat/src/modules/orders/lib/flutterwave.ts b/legacy/apps/tiamat/src/modules/orders/lib/flutterwave.ts similarity index 100% rename from apps/tiamat/src/modules/orders/lib/flutterwave.ts rename to legacy/apps/tiamat/src/modules/orders/lib/flutterwave.ts diff --git a/apps/tiamat/src/modules/orders/lib/order-meta.ts b/legacy/apps/tiamat/src/modules/orders/lib/order-meta.ts similarity index 100% rename from apps/tiamat/src/modules/orders/lib/order-meta.ts rename to legacy/apps/tiamat/src/modules/orders/lib/order-meta.ts diff --git a/apps/tiamat/src/modules/orders/lib/supabase.ts b/legacy/apps/tiamat/src/modules/orders/lib/supabase.ts similarity index 100% rename from apps/tiamat/src/modules/orders/lib/supabase.ts rename to legacy/apps/tiamat/src/modules/orders/lib/supabase.ts diff --git a/apps/tiamat/src/modules/orders/orders.controller.ts b/legacy/apps/tiamat/src/modules/orders/orders.controller.ts similarity index 100% rename from apps/tiamat/src/modules/orders/orders.controller.ts rename to legacy/apps/tiamat/src/modules/orders/orders.controller.ts diff --git a/apps/tiamat/src/modules/orders/orders.module.ts b/legacy/apps/tiamat/src/modules/orders/orders.module.ts similarity index 100% rename from apps/tiamat/src/modules/orders/orders.module.ts rename to legacy/apps/tiamat/src/modules/orders/orders.module.ts diff --git a/apps/tiamat/src/modules/orders/orders.service.ts b/legacy/apps/tiamat/src/modules/orders/orders.service.ts similarity index 100% rename from apps/tiamat/src/modules/orders/orders.service.ts rename to legacy/apps/tiamat/src/modules/orders/orders.service.ts diff --git a/apps/tiamat/src/modules/payouts/payouts.controller.ts b/legacy/apps/tiamat/src/modules/payouts/payouts.controller.ts similarity index 100% rename from apps/tiamat/src/modules/payouts/payouts.controller.ts rename to legacy/apps/tiamat/src/modules/payouts/payouts.controller.ts diff --git a/apps/tiamat/src/modules/payouts/payouts.module.ts b/legacy/apps/tiamat/src/modules/payouts/payouts.module.ts similarity index 100% rename from apps/tiamat/src/modules/payouts/payouts.module.ts rename to legacy/apps/tiamat/src/modules/payouts/payouts.module.ts diff --git a/apps/tiamat/src/modules/payouts/payouts.service.ts b/legacy/apps/tiamat/src/modules/payouts/payouts.service.ts similarity index 100% rename from apps/tiamat/src/modules/payouts/payouts.service.ts rename to legacy/apps/tiamat/src/modules/payouts/payouts.service.ts diff --git a/apps/tiamat/src/modules/pd-actions/dto/execute-pd-action.dto.ts b/legacy/apps/tiamat/src/modules/pd-actions/dto/execute-pd-action.dto.ts similarity index 100% rename from apps/tiamat/src/modules/pd-actions/dto/execute-pd-action.dto.ts rename to legacy/apps/tiamat/src/modules/pd-actions/dto/execute-pd-action.dto.ts diff --git a/apps/tiamat/src/modules/pd-actions/pd-actions.controller.ts b/legacy/apps/tiamat/src/modules/pd-actions/pd-actions.controller.ts similarity index 100% rename from apps/tiamat/src/modules/pd-actions/pd-actions.controller.ts rename to legacy/apps/tiamat/src/modules/pd-actions/pd-actions.controller.ts diff --git a/apps/tiamat/src/modules/pd-actions/pd-actions.module.ts b/legacy/apps/tiamat/src/modules/pd-actions/pd-actions.module.ts similarity index 100% rename from apps/tiamat/src/modules/pd-actions/pd-actions.module.ts rename to legacy/apps/tiamat/src/modules/pd-actions/pd-actions.module.ts diff --git a/apps/tiamat/src/modules/pd-actions/pd-actions.service.ts b/legacy/apps/tiamat/src/modules/pd-actions/pd-actions.service.ts similarity index 100% rename from apps/tiamat/src/modules/pd-actions/pd-actions.service.ts rename to legacy/apps/tiamat/src/modules/pd-actions/pd-actions.service.ts diff --git a/apps/tiamat/src/modules/pd-flows/dto/create-pd-flow.dto.ts b/legacy/apps/tiamat/src/modules/pd-flows/dto/create-pd-flow.dto.ts similarity index 100% rename from apps/tiamat/src/modules/pd-flows/dto/create-pd-flow.dto.ts rename to legacy/apps/tiamat/src/modules/pd-flows/dto/create-pd-flow.dto.ts diff --git a/apps/tiamat/src/modules/pd-flows/pd-flows.controller.ts b/legacy/apps/tiamat/src/modules/pd-flows/pd-flows.controller.ts similarity index 100% rename from apps/tiamat/src/modules/pd-flows/pd-flows.controller.ts rename to legacy/apps/tiamat/src/modules/pd-flows/pd-flows.controller.ts diff --git a/apps/tiamat/src/modules/pd-flows/pd-flows.module.ts b/legacy/apps/tiamat/src/modules/pd-flows/pd-flows.module.ts similarity index 100% rename from apps/tiamat/src/modules/pd-flows/pd-flows.module.ts rename to legacy/apps/tiamat/src/modules/pd-flows/pd-flows.module.ts diff --git a/apps/tiamat/src/modules/pd-flows/pd-flows.service.ts b/legacy/apps/tiamat/src/modules/pd-flows/pd-flows.service.ts similarity index 100% rename from apps/tiamat/src/modules/pd-flows/pd-flows.service.ts rename to legacy/apps/tiamat/src/modules/pd-flows/pd-flows.service.ts diff --git a/apps/tiamat/src/modules/sendgrid/sendgrid.controller.ts b/legacy/apps/tiamat/src/modules/sendgrid/sendgrid.controller.ts similarity index 100% rename from apps/tiamat/src/modules/sendgrid/sendgrid.controller.ts rename to legacy/apps/tiamat/src/modules/sendgrid/sendgrid.controller.ts diff --git a/apps/tiamat/src/modules/sendgrid/sendgrid.module.ts b/legacy/apps/tiamat/src/modules/sendgrid/sendgrid.module.ts similarity index 100% rename from apps/tiamat/src/modules/sendgrid/sendgrid.module.ts rename to legacy/apps/tiamat/src/modules/sendgrid/sendgrid.module.ts diff --git a/apps/tiamat/src/modules/sendgrid/sendgrid.service.ts b/legacy/apps/tiamat/src/modules/sendgrid/sendgrid.service.ts similarity index 100% rename from apps/tiamat/src/modules/sendgrid/sendgrid.service.ts rename to legacy/apps/tiamat/src/modules/sendgrid/sendgrid.service.ts diff --git a/apps/tiamat/src/modules/telegram/telegram.controller.ts b/legacy/apps/tiamat/src/modules/telegram/telegram.controller.ts similarity index 100% rename from apps/tiamat/src/modules/telegram/telegram.controller.ts rename to legacy/apps/tiamat/src/modules/telegram/telegram.controller.ts diff --git a/apps/tiamat/src/modules/telegram/telegram.module.ts b/legacy/apps/tiamat/src/modules/telegram/telegram.module.ts similarity index 100% rename from apps/tiamat/src/modules/telegram/telegram.module.ts rename to legacy/apps/tiamat/src/modules/telegram/telegram.module.ts diff --git a/apps/tiamat/src/modules/telegram/telegram.service.ts b/legacy/apps/tiamat/src/modules/telegram/telegram.service.ts similarity index 98% rename from apps/tiamat/src/modules/telegram/telegram.service.ts rename to legacy/apps/tiamat/src/modules/telegram/telegram.service.ts index 758be5b..a280790 100644 --- a/apps/tiamat/src/modules/telegram/telegram.service.ts +++ b/legacy/apps/tiamat/src/modules/telegram/telegram.service.ts @@ -123,11 +123,13 @@ export class TelegramService { const telegramNotification = { // This is used as the ID in FlowXO, to identify updates. // The 'directive_' prefix is a legacy thing. + // @OSS-MIGRATION :: Change to external_id directive_id: meterInteraction.id, // This is used as the main filter value in FlowXO to determine what to do. // Hardcode to PENDING because that's what FlowXO expects. // That and the 'directive_' prefix are a legacy thing. + // @OSS-MIGRATION :: Change to delivery_status directive_status: 'PENDING', grid_name: meter.connection?.customer?.grid?.name, diff --git a/apps/tiamat/src/modules/ussd-sessions/lib/supabase.ts b/legacy/apps/tiamat/src/modules/ussd-sessions/lib/supabase.ts similarity index 100% rename from apps/tiamat/src/modules/ussd-sessions/lib/supabase.ts rename to legacy/apps/tiamat/src/modules/ussd-sessions/lib/supabase.ts diff --git a/apps/tiamat/src/modules/ussd-sessions/ussd-sessions.module.ts b/legacy/apps/tiamat/src/modules/ussd-sessions/ussd-sessions.module.ts similarity index 100% rename from apps/tiamat/src/modules/ussd-sessions/ussd-sessions.module.ts rename to legacy/apps/tiamat/src/modules/ussd-sessions/ussd-sessions.module.ts diff --git a/apps/tiamat/src/modules/ussd-sessions/ussd-sessions.service.ts b/legacy/apps/tiamat/src/modules/ussd-sessions/ussd-sessions.service.ts similarity index 100% rename from apps/tiamat/src/modules/ussd-sessions/ussd-sessions.service.ts rename to legacy/apps/tiamat/src/modules/ussd-sessions/ussd-sessions.service.ts diff --git a/apps/tiamat/src/modules/wallets/wallets.module.ts b/legacy/apps/tiamat/src/modules/wallets/wallets.module.ts similarity index 100% rename from apps/tiamat/src/modules/wallets/wallets.module.ts rename to legacy/apps/tiamat/src/modules/wallets/wallets.module.ts diff --git a/apps/tiamat/src/modules/wallets/wallets.service.ts b/legacy/apps/tiamat/src/modules/wallets/wallets.service.ts similarity index 100% rename from apps/tiamat/src/modules/wallets/wallets.service.ts rename to legacy/apps/tiamat/src/modules/wallets/wallets.service.ts diff --git a/apps/tiamat/src/modules/websocket/websocket.gateway.ts b/legacy/apps/tiamat/src/modules/websocket/websocket.gateway.ts similarity index 100% rename from apps/tiamat/src/modules/websocket/websocket.gateway.ts rename to legacy/apps/tiamat/src/modules/websocket/websocket.gateway.ts diff --git a/apps/tiamat/src/modules/websocket/websocket.module.ts b/legacy/apps/tiamat/src/modules/websocket/websocket.module.ts similarity index 100% rename from apps/tiamat/src/modules/websocket/websocket.module.ts rename to legacy/apps/tiamat/src/modules/websocket/websocket.module.ts diff --git a/apps/tiamat/src/modules/websocket/websocket.service.ts b/legacy/apps/tiamat/src/modules/websocket/websocket.service.ts similarity index 100% rename from apps/tiamat/src/modules/websocket/websocket.service.ts rename to legacy/apps/tiamat/src/modules/websocket/websocket.service.ts diff --git a/apps/tiamat/src/queries/index.ts b/legacy/apps/tiamat/src/queries/index.ts similarity index 100% rename from apps/tiamat/src/queries/index.ts rename to legacy/apps/tiamat/src/queries/index.ts diff --git a/apps/tiamat/src/queries/lua/device-messages/fetch-next-message-in-queue.lua b/legacy/apps/tiamat/src/queries/lua/device-messages/fetch-next-message-in-queue.lua similarity index 100% rename from apps/tiamat/src/queries/lua/device-messages/fetch-next-message-in-queue.lua rename to legacy/apps/tiamat/src/queries/lua/device-messages/fetch-next-message-in-queue.lua diff --git a/apps/tiamat/src/queries/lua/device-messages/fetch-next-message-in-queue.types.ts b/legacy/apps/tiamat/src/queries/lua/device-messages/fetch-next-message-in-queue.types.ts similarity index 100% rename from apps/tiamat/src/queries/lua/device-messages/fetch-next-message-in-queue.types.ts rename to legacy/apps/tiamat/src/queries/lua/device-messages/fetch-next-message-in-queue.types.ts diff --git a/apps/tiamat/src/queries/lua/device-messages/move-message-between-queues.lua b/legacy/apps/tiamat/src/queries/lua/device-messages/move-message-between-queues.lua similarity index 100% rename from apps/tiamat/src/queries/lua/device-messages/move-message-between-queues.lua rename to legacy/apps/tiamat/src/queries/lua/device-messages/move-message-between-queues.lua diff --git a/apps/tiamat/src/queries/lua/device-messages/move-message-between-queues.types.ts b/legacy/apps/tiamat/src/queries/lua/device-messages/move-message-between-queues.types.ts similarity index 100% rename from apps/tiamat/src/queries/lua/device-messages/move-message-between-queues.types.ts rename to legacy/apps/tiamat/src/queries/lua/device-messages/move-message-between-queues.types.ts diff --git a/apps/tiamat/src/queries/sql/supabase/directive-batch-executions/count-statuses.sql b/legacy/apps/tiamat/src/queries/sql/supabase/directive-batch-executions/count-statuses.sql similarity index 100% rename from apps/tiamat/src/queries/sql/supabase/directive-batch-executions/count-statuses.sql rename to legacy/apps/tiamat/src/queries/sql/supabase/directive-batch-executions/count-statuses.sql diff --git a/apps/tiamat/src/queries/sql/supabase/directive-batch-executions/count-statuses.types.ts b/legacy/apps/tiamat/src/queries/sql/supabase/directive-batch-executions/count-statuses.types.ts similarity index 100% rename from apps/tiamat/src/queries/sql/supabase/directive-batch-executions/count-statuses.types.ts rename to legacy/apps/tiamat/src/queries/sql/supabase/directive-batch-executions/count-statuses.types.ts diff --git a/apps/tiamat/src/queries/sql/supabase/meter-commissionings/lock-next.sql b/legacy/apps/tiamat/src/queries/sql/supabase/meter-commissionings/lock-next.sql similarity index 100% rename from apps/tiamat/src/queries/sql/supabase/meter-commissionings/lock-next.sql rename to legacy/apps/tiamat/src/queries/sql/supabase/meter-commissionings/lock-next.sql diff --git a/apps/tiamat/src/queries/sql/supabase/meter-commissionings/lock-next.types.ts b/legacy/apps/tiamat/src/queries/sql/supabase/meter-commissionings/lock-next.types.ts similarity index 100% rename from apps/tiamat/src/queries/sql/supabase/meter-commissionings/lock-next.types.ts rename to legacy/apps/tiamat/src/queries/sql/supabase/meter-commissionings/lock-next.types.ts diff --git a/apps/tiamat/src/queries/sql/supabase/meters/lock-issues-page.sql b/legacy/apps/tiamat/src/queries/sql/supabase/meters/lock-issues-page.sql similarity index 100% rename from apps/tiamat/src/queries/sql/supabase/meters/lock-issues-page.sql rename to legacy/apps/tiamat/src/queries/sql/supabase/meters/lock-issues-page.sql diff --git a/apps/tiamat/src/queries/sql/supabase/meters/lock-issues-page.types.ts b/legacy/apps/tiamat/src/queries/sql/supabase/meters/lock-issues-page.types.ts similarity index 100% rename from apps/tiamat/src/queries/sql/supabase/meters/lock-issues-page.types.ts rename to legacy/apps/tiamat/src/queries/sql/supabase/meters/lock-issues-page.types.ts diff --git a/apps/tiamat/src/queries/sql/timescale/data-analytics/get-top-consumers.sql b/legacy/apps/tiamat/src/queries/sql/timescale/data-analytics/get-top-consumers.sql similarity index 100% rename from apps/tiamat/src/queries/sql/timescale/data-analytics/get-top-consumers.sql rename to legacy/apps/tiamat/src/queries/sql/timescale/data-analytics/get-top-consumers.sql diff --git a/apps/tiamat/src/queries/sql/timescale/data-analytics/get-top-consumers.types.ts b/legacy/apps/tiamat/src/queries/sql/timescale/data-analytics/get-top-consumers.types.ts similarity index 100% rename from apps/tiamat/src/queries/sql/timescale/data-analytics/get-top-consumers.types.ts rename to legacy/apps/tiamat/src/queries/sql/timescale/data-analytics/get-top-consumers.types.ts diff --git a/apps/tiamat/src/queries/sql/timescale/data-analytics/get-uptimes.sql b/legacy/apps/tiamat/src/queries/sql/timescale/data-analytics/get-uptimes.sql similarity index 100% rename from apps/tiamat/src/queries/sql/timescale/data-analytics/get-uptimes.sql rename to legacy/apps/tiamat/src/queries/sql/timescale/data-analytics/get-uptimes.sql diff --git a/apps/tiamat/src/queries/sql/timescale/data-analytics/get-uptimes.types.ts b/legacy/apps/tiamat/src/queries/sql/timescale/data-analytics/get-uptimes.types.ts similarity index 100% rename from apps/tiamat/src/queries/sql/timescale/data-analytics/get-uptimes.types.ts rename to legacy/apps/tiamat/src/queries/sql/timescale/data-analytics/get-uptimes.types.ts diff --git a/apps/tiamat/src/queries/sql/timescale/lost-revenue/get-avg-consumption.sql b/legacy/apps/tiamat/src/queries/sql/timescale/lost-revenue/get-avg-consumption.sql similarity index 100% rename from apps/tiamat/src/queries/sql/timescale/lost-revenue/get-avg-consumption.sql rename to legacy/apps/tiamat/src/queries/sql/timescale/lost-revenue/get-avg-consumption.sql diff --git a/apps/tiamat/src/queries/sql/timescale/lost-revenue/get-avg-consumption.types.ts b/legacy/apps/tiamat/src/queries/sql/timescale/lost-revenue/get-avg-consumption.types.ts similarity index 100% rename from apps/tiamat/src/queries/sql/timescale/lost-revenue/get-avg-consumption.types.ts rename to legacy/apps/tiamat/src/queries/sql/timescale/lost-revenue/get-avg-consumption.types.ts diff --git a/apps/tiamat/src/queries/sql/timescale/payouts/find-exchange-rate.sql b/legacy/apps/tiamat/src/queries/sql/timescale/payouts/find-exchange-rate.sql similarity index 100% rename from apps/tiamat/src/queries/sql/timescale/payouts/find-exchange-rate.sql rename to legacy/apps/tiamat/src/queries/sql/timescale/payouts/find-exchange-rate.sql diff --git a/apps/tiamat/src/queries/sql/timescale/payouts/find-exchange-rate.types.ts b/legacy/apps/tiamat/src/queries/sql/timescale/payouts/find-exchange-rate.types.ts similarity index 100% rename from apps/tiamat/src/queries/sql/timescale/payouts/find-exchange-rate.types.ts rename to legacy/apps/tiamat/src/queries/sql/timescale/payouts/find-exchange-rate.types.ts diff --git a/apps/tiamat/src/queries/sql/timescale/payouts/get-mppt-uptimes.sql b/legacy/apps/tiamat/src/queries/sql/timescale/payouts/get-mppt-uptimes.sql similarity index 100% rename from apps/tiamat/src/queries/sql/timescale/payouts/get-mppt-uptimes.sql rename to legacy/apps/tiamat/src/queries/sql/timescale/payouts/get-mppt-uptimes.sql diff --git a/apps/tiamat/src/queries/sql/timescale/payouts/get-mppt-uptimes.types.ts b/legacy/apps/tiamat/src/queries/sql/timescale/payouts/get-mppt-uptimes.types.ts similarity index 100% rename from apps/tiamat/src/queries/sql/timescale/payouts/get-mppt-uptimes.types.ts rename to legacy/apps/tiamat/src/queries/sql/timescale/payouts/get-mppt-uptimes.types.ts diff --git a/apps/tiamat/tsconfig.app.json b/legacy/apps/tiamat/tsconfig.app.json similarity index 100% rename from apps/tiamat/tsconfig.app.json rename to legacy/apps/tiamat/tsconfig.app.json diff --git a/apps/tiamat/tsconfig.json b/legacy/apps/tiamat/tsconfig.json similarity index 100% rename from apps/tiamat/tsconfig.json rename to legacy/apps/tiamat/tsconfig.json diff --git a/apps/tiamat/tsconfig.spec.json b/legacy/apps/tiamat/tsconfig.spec.json similarity index 100% rename from apps/tiamat/tsconfig.spec.json rename to legacy/apps/tiamat/tsconfig.spec.json diff --git a/apps/tiamat/webpack.config.js b/legacy/apps/tiamat/webpack.config.js similarity index 100% rename from apps/tiamat/webpack.config.js rename to legacy/apps/tiamat/webpack.config.js diff --git a/apps/yeti/.env.example b/legacy/apps/yeti/.env.example similarity index 100% rename from apps/yeti/.env.example rename to legacy/apps/yeti/.env.example diff --git a/apps/yeti/README.md b/legacy/apps/yeti/README.md similarity index 100% rename from apps/yeti/README.md rename to legacy/apps/yeti/README.md diff --git a/apps/yeti/jest.config.ts b/legacy/apps/yeti/jest.config.ts similarity index 100% rename from apps/yeti/jest.config.ts rename to legacy/apps/yeti/jest.config.ts diff --git a/apps/yeti/project.json b/legacy/apps/yeti/project.json similarity index 100% rename from apps/yeti/project.json rename to legacy/apps/yeti/project.json diff --git a/apps/yeti/src/main.ts b/legacy/apps/yeti/src/main.ts similarity index 100% rename from apps/yeti/src/main.ts rename to legacy/apps/yeti/src/main.ts diff --git a/apps/yeti/src/modules/app.module.ts b/legacy/apps/yeti/src/modules/app.module.ts similarity index 100% rename from apps/yeti/src/modules/app.module.ts rename to legacy/apps/yeti/src/modules/app.module.ts diff --git a/apps/yeti/src/modules/calin/calin.module.ts b/legacy/apps/yeti/src/modules/calin/calin.module.ts similarity index 100% rename from apps/yeti/src/modules/calin/calin.module.ts rename to legacy/apps/yeti/src/modules/calin/calin.module.ts diff --git a/apps/yeti/src/modules/calin/calin.service.ts b/legacy/apps/yeti/src/modules/calin/calin.service.ts similarity index 100% rename from apps/yeti/src/modules/calin/calin.service.ts rename to legacy/apps/yeti/src/modules/calin/calin.service.ts diff --git a/apps/yeti/src/modules/calin/implementations/calin-lorawan.service.ts b/legacy/apps/yeti/src/modules/calin/implementations/calin-lorawan.service.ts similarity index 100% rename from apps/yeti/src/modules/calin/implementations/calin-lorawan.service.ts rename to legacy/apps/yeti/src/modules/calin/implementations/calin-lorawan.service.ts diff --git a/apps/yeti/src/modules/calin/implementations/calinv1.service.ts b/legacy/apps/yeti/src/modules/calin/implementations/calinv1.service.ts similarity index 100% rename from apps/yeti/src/modules/calin/implementations/calinv1.service.ts rename to legacy/apps/yeti/src/modules/calin/implementations/calinv1.service.ts diff --git a/apps/yeti/src/modules/calin/implementations/calinv2.service.ts b/legacy/apps/yeti/src/modules/calin/implementations/calinv2.service.ts similarity index 100% rename from apps/yeti/src/modules/calin/implementations/calinv2.service.ts rename to legacy/apps/yeti/src/modules/calin/implementations/calinv2.service.ts diff --git a/apps/yeti/src/modules/dcu-snapshot-1-min/dcu-snapshot-1-min.module.ts b/legacy/apps/yeti/src/modules/dcu-snapshot-1-min/dcu-snapshot-1-min.module.ts similarity index 100% rename from apps/yeti/src/modules/dcu-snapshot-1-min/dcu-snapshot-1-min.module.ts rename to legacy/apps/yeti/src/modules/dcu-snapshot-1-min/dcu-snapshot-1-min.module.ts diff --git a/apps/yeti/src/modules/dcu-snapshot-1-min/dcu-snapshot-1-min.service.ts b/legacy/apps/yeti/src/modules/dcu-snapshot-1-min/dcu-snapshot-1-min.service.ts similarity index 100% rename from apps/yeti/src/modules/dcu-snapshot-1-min/dcu-snapshot-1-min.service.ts rename to legacy/apps/yeti/src/modules/dcu-snapshot-1-min/dcu-snapshot-1-min.service.ts diff --git a/apps/yeti/src/modules/dcu-snapshot-1-min/dto/calin-concentrator.ts b/legacy/apps/yeti/src/modules/dcu-snapshot-1-min/dto/calin-concentrator.ts similarity index 100% rename from apps/yeti/src/modules/dcu-snapshot-1-min/dto/calin-concentrator.ts rename to legacy/apps/yeti/src/modules/dcu-snapshot-1-min/dto/calin-concentrator.ts diff --git a/apps/yeti/src/modules/device-data-sink/device-data-sink.controller.ts b/legacy/apps/yeti/src/modules/device-data-sink/device-data-sink.controller.ts similarity index 100% rename from apps/yeti/src/modules/device-data-sink/device-data-sink.controller.ts rename to legacy/apps/yeti/src/modules/device-data-sink/device-data-sink.controller.ts diff --git a/apps/yeti/src/modules/device-data-sink/device-data-sink.module.ts b/legacy/apps/yeti/src/modules/device-data-sink/device-data-sink.module.ts similarity index 100% rename from apps/yeti/src/modules/device-data-sink/device-data-sink.module.ts rename to legacy/apps/yeti/src/modules/device-data-sink/device-data-sink.module.ts diff --git a/apps/yeti/src/modules/device-data-sink/device-data-sink.service.ts b/legacy/apps/yeti/src/modules/device-data-sink/device-data-sink.service.ts similarity index 100% rename from apps/yeti/src/modules/device-data-sink/device-data-sink.service.ts rename to legacy/apps/yeti/src/modules/device-data-sink/device-data-sink.service.ts diff --git a/apps/yeti/src/modules/exchange-rate-snapshot-1-d/exchange-rate-snapshot-1-d.module.ts b/legacy/apps/yeti/src/modules/exchange-rate-snapshot-1-d/exchange-rate-snapshot-1-d.module.ts similarity index 100% rename from apps/yeti/src/modules/exchange-rate-snapshot-1-d/exchange-rate-snapshot-1-d.module.ts rename to legacy/apps/yeti/src/modules/exchange-rate-snapshot-1-d/exchange-rate-snapshot-1-d.module.ts diff --git a/apps/yeti/src/modules/exchange-rate-snapshot-1-d/exchange-rate-snapshot-1-d.service.ts b/legacy/apps/yeti/src/modules/exchange-rate-snapshot-1-d/exchange-rate-snapshot-1-d.service.ts similarity index 100% rename from apps/yeti/src/modules/exchange-rate-snapshot-1-d/exchange-rate-snapshot-1-d.service.ts rename to legacy/apps/yeti/src/modules/exchange-rate-snapshot-1-d/exchange-rate-snapshot-1-d.service.ts diff --git a/apps/yeti/src/modules/exchange-rates/exchange-rates.module.ts b/legacy/apps/yeti/src/modules/exchange-rates/exchange-rates.module.ts similarity index 100% rename from apps/yeti/src/modules/exchange-rates/exchange-rates.module.ts rename to legacy/apps/yeti/src/modules/exchange-rates/exchange-rates.module.ts diff --git a/apps/yeti/src/modules/exchange-rates/exchange-rates.service.ts b/legacy/apps/yeti/src/modules/exchange-rates/exchange-rates.service.ts similarity index 100% rename from apps/yeti/src/modules/exchange-rates/exchange-rates.service.ts rename to legacy/apps/yeti/src/modules/exchange-rates/exchange-rates.service.ts diff --git a/apps/yeti/src/modules/grid-business-snapshot-1-d/grid-business-snapshot-1-d.module.ts b/legacy/apps/yeti/src/modules/grid-business-snapshot-1-d/grid-business-snapshot-1-d.module.ts similarity index 100% rename from apps/yeti/src/modules/grid-business-snapshot-1-d/grid-business-snapshot-1-d.module.ts rename to legacy/apps/yeti/src/modules/grid-business-snapshot-1-d/grid-business-snapshot-1-d.module.ts diff --git a/apps/yeti/src/modules/grid-business-snapshot-1-d/grid-business-snapshot-1-d.service.ts b/legacy/apps/yeti/src/modules/grid-business-snapshot-1-d/grid-business-snapshot-1-d.service.ts similarity index 100% rename from apps/yeti/src/modules/grid-business-snapshot-1-d/grid-business-snapshot-1-d.service.ts rename to legacy/apps/yeti/src/modules/grid-business-snapshot-1-d/grid-business-snapshot-1-d.service.ts diff --git a/apps/yeti/src/modules/grid-business-snapshot-1-d/lib/issue-counting.ts b/legacy/apps/yeti/src/modules/grid-business-snapshot-1-d/lib/issue-counting.ts similarity index 100% rename from apps/yeti/src/modules/grid-business-snapshot-1-d/lib/issue-counting.ts rename to legacy/apps/yeti/src/modules/grid-business-snapshot-1-d/lib/issue-counting.ts diff --git a/apps/yeti/src/modules/grid-diagnostics/grid-diagnostics.module.ts b/legacy/apps/yeti/src/modules/grid-diagnostics/grid-diagnostics.module.ts similarity index 100% rename from apps/yeti/src/modules/grid-diagnostics/grid-diagnostics.module.ts rename to legacy/apps/yeti/src/modules/grid-diagnostics/grid-diagnostics.module.ts diff --git a/apps/yeti/src/modules/grid-diagnostics/grid-diagnostics.service.ts b/legacy/apps/yeti/src/modules/grid-diagnostics/grid-diagnostics.service.ts similarity index 100% rename from apps/yeti/src/modules/grid-diagnostics/grid-diagnostics.service.ts rename to legacy/apps/yeti/src/modules/grid-diagnostics/grid-diagnostics.service.ts diff --git a/apps/yeti/src/modules/grid-digital-twin/grid-digital-twin.module.ts b/legacy/apps/yeti/src/modules/grid-digital-twin/grid-digital-twin.module.ts similarity index 100% rename from apps/yeti/src/modules/grid-digital-twin/grid-digital-twin.module.ts rename to legacy/apps/yeti/src/modules/grid-digital-twin/grid-digital-twin.module.ts diff --git a/apps/yeti/src/modules/grid-digital-twin/grid-digital-twin.service.ts b/legacy/apps/yeti/src/modules/grid-digital-twin/grid-digital-twin.service.ts similarity index 100% rename from apps/yeti/src/modules/grid-digital-twin/grid-digital-twin.service.ts rename to legacy/apps/yeti/src/modules/grid-digital-twin/grid-digital-twin.service.ts diff --git a/apps/yeti/src/modules/grid-digital-twin/lib/mqtt-helpers.ts b/legacy/apps/yeti/src/modules/grid-digital-twin/lib/mqtt-helpers.ts similarity index 100% rename from apps/yeti/src/modules/grid-digital-twin/lib/mqtt-helpers.ts rename to legacy/apps/yeti/src/modules/grid-digital-twin/lib/mqtt-helpers.ts diff --git a/apps/yeti/src/modules/grid-energy-snapshot-15-min/grid-energy-snapshot-15-min.module.ts b/legacy/apps/yeti/src/modules/grid-energy-snapshot-15-min/grid-energy-snapshot-15-min.module.ts similarity index 100% rename from apps/yeti/src/modules/grid-energy-snapshot-15-min/grid-energy-snapshot-15-min.module.ts rename to legacy/apps/yeti/src/modules/grid-energy-snapshot-15-min/grid-energy-snapshot-15-min.module.ts diff --git a/apps/yeti/src/modules/grid-energy-snapshot-15-min/grid-energy-snapshot-15-min.service.ts b/legacy/apps/yeti/src/modules/grid-energy-snapshot-15-min/grid-energy-snapshot-15-min.service.ts similarity index 100% rename from apps/yeti/src/modules/grid-energy-snapshot-15-min/grid-energy-snapshot-15-min.service.ts rename to legacy/apps/yeti/src/modules/grid-energy-snapshot-15-min/grid-energy-snapshot-15-min.service.ts diff --git a/apps/yeti/src/modules/grid-forecast-snapshot-1-h/grid-forecast-snapshot-1-h.module.ts b/legacy/apps/yeti/src/modules/grid-forecast-snapshot-1-h/grid-forecast-snapshot-1-h.module.ts similarity index 100% rename from apps/yeti/src/modules/grid-forecast-snapshot-1-h/grid-forecast-snapshot-1-h.module.ts rename to legacy/apps/yeti/src/modules/grid-forecast-snapshot-1-h/grid-forecast-snapshot-1-h.module.ts diff --git a/apps/yeti/src/modules/grid-forecast-snapshot-1-h/grid-forecast-snapshot-1-h.service.ts b/legacy/apps/yeti/src/modules/grid-forecast-snapshot-1-h/grid-forecast-snapshot-1-h.service.ts similarity index 100% rename from apps/yeti/src/modules/grid-forecast-snapshot-1-h/grid-forecast-snapshot-1-h.service.ts rename to legacy/apps/yeti/src/modules/grid-forecast-snapshot-1-h/grid-forecast-snapshot-1-h.service.ts diff --git a/apps/yeti/src/modules/meter-consumption/README.md b/legacy/apps/yeti/src/modules/meter-consumption/README.md similarity index 100% rename from apps/yeti/src/modules/meter-consumption/README.md rename to legacy/apps/yeti/src/modules/meter-consumption/README.md diff --git a/apps/yeti/src/modules/meter-consumption/api-key.guard.ts b/legacy/apps/yeti/src/modules/meter-consumption/api-key.guard.ts similarity index 100% rename from apps/yeti/src/modules/meter-consumption/api-key.guard.ts rename to legacy/apps/yeti/src/modules/meter-consumption/api-key.guard.ts diff --git a/apps/yeti/src/modules/meter-consumption/meter-consumption.controller.ts b/legacy/apps/yeti/src/modules/meter-consumption/meter-consumption.controller.ts similarity index 100% rename from apps/yeti/src/modules/meter-consumption/meter-consumption.controller.ts rename to legacy/apps/yeti/src/modules/meter-consumption/meter-consumption.controller.ts diff --git a/apps/yeti/src/modules/meter-consumption/meter-consumption.module.ts b/legacy/apps/yeti/src/modules/meter-consumption/meter-consumption.module.ts similarity index 100% rename from apps/yeti/src/modules/meter-consumption/meter-consumption.module.ts rename to legacy/apps/yeti/src/modules/meter-consumption/meter-consumption.module.ts diff --git a/apps/yeti/src/modules/meter-consumption/meter-consumption.service.ts b/legacy/apps/yeti/src/modules/meter-consumption/meter-consumption.service.ts similarity index 100% rename from apps/yeti/src/modules/meter-consumption/meter-consumption.service.ts rename to legacy/apps/yeti/src/modules/meter-consumption/meter-consumption.service.ts diff --git a/apps/yeti/src/modules/meter-snapshot-1-h/meter-snapshot-1-h.controller.ts b/legacy/apps/yeti/src/modules/meter-snapshot-1-h/meter-snapshot-1-h.controller.ts similarity index 100% rename from apps/yeti/src/modules/meter-snapshot-1-h/meter-snapshot-1-h.controller.ts rename to legacy/apps/yeti/src/modules/meter-snapshot-1-h/meter-snapshot-1-h.controller.ts diff --git a/apps/yeti/src/modules/meter-snapshot-1-h/meter-snapshot-1-h.module.ts b/legacy/apps/yeti/src/modules/meter-snapshot-1-h/meter-snapshot-1-h.module.ts similarity index 100% rename from apps/yeti/src/modules/meter-snapshot-1-h/meter-snapshot-1-h.module.ts rename to legacy/apps/yeti/src/modules/meter-snapshot-1-h/meter-snapshot-1-h.module.ts diff --git a/apps/yeti/src/modules/meter-snapshot-1-h/meter-snapshot-1-h.service.ts b/legacy/apps/yeti/src/modules/meter-snapshot-1-h/meter-snapshot-1-h.service.ts similarity index 100% rename from apps/yeti/src/modules/meter-snapshot-1-h/meter-snapshot-1-h.service.ts rename to legacy/apps/yeti/src/modules/meter-snapshot-1-h/meter-snapshot-1-h.service.ts diff --git a/apps/yeti/src/modules/mppt-asset-snapshot-1-d/mppt-asset-snapshot-1-d.module.ts b/legacy/apps/yeti/src/modules/mppt-asset-snapshot-1-d/mppt-asset-snapshot-1-d.module.ts similarity index 100% rename from apps/yeti/src/modules/mppt-asset-snapshot-1-d/mppt-asset-snapshot-1-d.module.ts rename to legacy/apps/yeti/src/modules/mppt-asset-snapshot-1-d/mppt-asset-snapshot-1-d.module.ts diff --git a/apps/yeti/src/modules/mppt-asset-snapshot-1-d/mppt-asset-snapshot-1-d.service.ts b/legacy/apps/yeti/src/modules/mppt-asset-snapshot-1-d/mppt-asset-snapshot-1-d.service.ts similarity index 100% rename from apps/yeti/src/modules/mppt-asset-snapshot-1-d/mppt-asset-snapshot-1-d.service.ts rename to legacy/apps/yeti/src/modules/mppt-asset-snapshot-1-d/mppt-asset-snapshot-1-d.service.ts diff --git a/apps/yeti/src/modules/mppt-energy-snapshot-15-min/mppt-energy-snapshot-15-min.module.ts b/legacy/apps/yeti/src/modules/mppt-energy-snapshot-15-min/mppt-energy-snapshot-15-min.module.ts similarity index 100% rename from apps/yeti/src/modules/mppt-energy-snapshot-15-min/mppt-energy-snapshot-15-min.module.ts rename to legacy/apps/yeti/src/modules/mppt-energy-snapshot-15-min/mppt-energy-snapshot-15-min.module.ts diff --git a/apps/yeti/src/modules/mppt-energy-snapshot-15-min/mppt-energy-snapshot-15-min.service.ts b/legacy/apps/yeti/src/modules/mppt-energy-snapshot-15-min/mppt-energy-snapshot-15-min.service.ts similarity index 100% rename from apps/yeti/src/modules/mppt-energy-snapshot-15-min/mppt-energy-snapshot-15-min.service.ts rename to legacy/apps/yeti/src/modules/mppt-energy-snapshot-15-min/mppt-energy-snapshot-15-min.service.ts diff --git a/apps/yeti/src/modules/mppt-estimated-actual-snapshot-30-min/mppt-estimated-actual-snapshot-30-min.module.ts b/legacy/apps/yeti/src/modules/mppt-estimated-actual-snapshot-30-min/mppt-estimated-actual-snapshot-30-min.module.ts similarity index 100% rename from apps/yeti/src/modules/mppt-estimated-actual-snapshot-30-min/mppt-estimated-actual-snapshot-30-min.module.ts rename to legacy/apps/yeti/src/modules/mppt-estimated-actual-snapshot-30-min/mppt-estimated-actual-snapshot-30-min.module.ts diff --git a/apps/yeti/src/modules/mppt-estimated-actual-snapshot-30-min/mppt-estimated-actual-snapshot-30-min.service.ts b/legacy/apps/yeti/src/modules/mppt-estimated-actual-snapshot-30-min/mppt-estimated-actual-snapshot-30-min.service.ts similarity index 100% rename from apps/yeti/src/modules/mppt-estimated-actual-snapshot-30-min/mppt-estimated-actual-snapshot-30-min.service.ts rename to legacy/apps/yeti/src/modules/mppt-estimated-actual-snapshot-30-min/mppt-estimated-actual-snapshot-30-min.service.ts diff --git a/apps/yeti/src/modules/mppt-forecast-snapshot-30-min/mppt-forecast-snapshot-30-min.module.ts b/legacy/apps/yeti/src/modules/mppt-forecast-snapshot-30-min/mppt-forecast-snapshot-30-min.module.ts similarity index 100% rename from apps/yeti/src/modules/mppt-forecast-snapshot-30-min/mppt-forecast-snapshot-30-min.module.ts rename to legacy/apps/yeti/src/modules/mppt-forecast-snapshot-30-min/mppt-forecast-snapshot-30-min.module.ts diff --git a/apps/yeti/src/modules/mppt-forecast-snapshot-30-min/mppt-forecast-snapshot-30-min.service.ts b/legacy/apps/yeti/src/modules/mppt-forecast-snapshot-30-min/mppt-forecast-snapshot-30-min.service.ts similarity index 100% rename from apps/yeti/src/modules/mppt-forecast-snapshot-30-min/mppt-forecast-snapshot-30-min.service.ts rename to legacy/apps/yeti/src/modules/mppt-forecast-snapshot-30-min/mppt-forecast-snapshot-30-min.service.ts diff --git a/apps/yeti/src/modules/mppts/mppts.module.ts b/legacy/apps/yeti/src/modules/mppts/mppts.module.ts similarity index 100% rename from apps/yeti/src/modules/mppts/mppts.module.ts rename to legacy/apps/yeti/src/modules/mppts/mppts.module.ts diff --git a/apps/yeti/src/modules/organization-snapshot-1-d/organization-snapshot-1-d.module.ts b/legacy/apps/yeti/src/modules/organization-snapshot-1-d/organization-snapshot-1-d.module.ts similarity index 100% rename from apps/yeti/src/modules/organization-snapshot-1-d/organization-snapshot-1-d.module.ts rename to legacy/apps/yeti/src/modules/organization-snapshot-1-d/organization-snapshot-1-d.module.ts diff --git a/apps/yeti/src/modules/organization-snapshot-1-d/organization-snapshot-1-d.service.ts b/legacy/apps/yeti/src/modules/organization-snapshot-1-d/organization-snapshot-1-d.service.ts similarity index 100% rename from apps/yeti/src/modules/organization-snapshot-1-d/organization-snapshot-1-d.service.ts rename to legacy/apps/yeti/src/modules/organization-snapshot-1-d/organization-snapshot-1-d.service.ts diff --git a/apps/yeti/src/modules/router-snapshot-1-min/router-snapshot-1-min.module.ts b/legacy/apps/yeti/src/modules/router-snapshot-1-min/router-snapshot-1-min.module.ts similarity index 100% rename from apps/yeti/src/modules/router-snapshot-1-min/router-snapshot-1-min.module.ts rename to legacy/apps/yeti/src/modules/router-snapshot-1-min/router-snapshot-1-min.module.ts diff --git a/apps/yeti/src/modules/router-snapshot-1-min/router-snapshot-1-min.service.ts b/legacy/apps/yeti/src/modules/router-snapshot-1-min/router-snapshot-1-min.service.ts similarity index 100% rename from apps/yeti/src/modules/router-snapshot-1-min/router-snapshot-1-min.service.ts rename to legacy/apps/yeti/src/modules/router-snapshot-1-min/router-snapshot-1-min.service.ts diff --git a/apps/yeti/src/modules/router-snapshot-1-min/routers.service.ts b/legacy/apps/yeti/src/modules/router-snapshot-1-min/routers.service.ts similarity index 100% rename from apps/yeti/src/modules/router-snapshot-1-min/routers.service.ts rename to legacy/apps/yeti/src/modules/router-snapshot-1-min/routers.service.ts diff --git a/apps/yeti/src/modules/router-snapshot-1-min/types/zerotier-datapoint.ts b/legacy/apps/yeti/src/modules/router-snapshot-1-min/types/zerotier-datapoint.ts similarity index 100% rename from apps/yeti/src/modules/router-snapshot-1-min/types/zerotier-datapoint.ts rename to legacy/apps/yeti/src/modules/router-snapshot-1-min/types/zerotier-datapoint.ts diff --git a/apps/yeti/src/modules/routers/routers.module.ts b/legacy/apps/yeti/src/modules/routers/routers.module.ts similarity index 100% rename from apps/yeti/src/modules/routers/routers.module.ts rename to legacy/apps/yeti/src/modules/routers/routers.module.ts diff --git a/apps/yeti/src/modules/solcast-cache/solcast-cache.module.ts b/legacy/apps/yeti/src/modules/solcast-cache/solcast-cache.module.ts similarity index 100% rename from apps/yeti/src/modules/solcast-cache/solcast-cache.module.ts rename to legacy/apps/yeti/src/modules/solcast-cache/solcast-cache.module.ts diff --git a/apps/yeti/src/modules/solcast-cache/solcast-cache.service.ts b/legacy/apps/yeti/src/modules/solcast-cache/solcast-cache.service.ts similarity index 100% rename from apps/yeti/src/modules/solcast-cache/solcast-cache.service.ts rename to legacy/apps/yeti/src/modules/solcast-cache/solcast-cache.service.ts diff --git a/apps/yeti/src/modules/solcast/solcast.module.ts b/legacy/apps/yeti/src/modules/solcast/solcast.module.ts similarity index 100% rename from apps/yeti/src/modules/solcast/solcast.module.ts rename to legacy/apps/yeti/src/modules/solcast/solcast.module.ts diff --git a/apps/yeti/src/modules/solcast/solcast.service.ts b/legacy/apps/yeti/src/modules/solcast/solcast.service.ts similarity index 100% rename from apps/yeti/src/modules/solcast/solcast.service.ts rename to legacy/apps/yeti/src/modules/solcast/solcast.service.ts diff --git a/apps/yeti/src/modules/solcast/types/solcast-response.ts b/legacy/apps/yeti/src/modules/solcast/types/solcast-response.ts similarity index 100% rename from apps/yeti/src/modules/solcast/types/solcast-response.ts rename to legacy/apps/yeti/src/modules/solcast/types/solcast-response.ts diff --git a/apps/yeti/src/modules/zerotier/zerotier.module.ts b/legacy/apps/yeti/src/modules/zerotier/zerotier.module.ts similarity index 100% rename from apps/yeti/src/modules/zerotier/zerotier.module.ts rename to legacy/apps/yeti/src/modules/zerotier/zerotier.module.ts diff --git a/apps/yeti/src/modules/zerotier/zerotier.service.ts b/legacy/apps/yeti/src/modules/zerotier/zerotier.service.ts similarity index 100% rename from apps/yeti/src/modules/zerotier/zerotier.service.ts rename to legacy/apps/yeti/src/modules/zerotier/zerotier.service.ts diff --git a/apps/yeti/src/queries/index.ts b/legacy/apps/yeti/src/queries/index.ts similarity index 100% rename from apps/yeti/src/queries/index.ts rename to legacy/apps/yeti/src/queries/index.ts diff --git a/apps/yeti/src/queries/sql/supabase/dcu-snapshot-1-min/get-dcus-with-load-warning.sql b/legacy/apps/yeti/src/queries/sql/supabase/dcu-snapshot-1-min/get-dcus-with-load-warning.sql similarity index 100% rename from apps/yeti/src/queries/sql/supabase/dcu-snapshot-1-min/get-dcus-with-load-warning.sql rename to legacy/apps/yeti/src/queries/sql/supabase/dcu-snapshot-1-min/get-dcus-with-load-warning.sql diff --git a/apps/yeti/src/queries/sql/supabase/dcu-snapshot-1-min/get-dcus-with-load-warning.types.ts b/legacy/apps/yeti/src/queries/sql/supabase/dcu-snapshot-1-min/get-dcus-with-load-warning.types.ts similarity index 100% rename from apps/yeti/src/queries/sql/supabase/dcu-snapshot-1-min/get-dcus-with-load-warning.types.ts rename to legacy/apps/yeti/src/queries/sql/supabase/dcu-snapshot-1-min/get-dcus-with-load-warning.types.ts diff --git a/apps/yeti/src/queries/sql/supabase/organization-snapshot-1-d/find-grouped-by-organization.sql b/legacy/apps/yeti/src/queries/sql/supabase/organization-snapshot-1-d/find-grouped-by-organization.sql similarity index 100% rename from apps/yeti/src/queries/sql/supabase/organization-snapshot-1-d/find-grouped-by-organization.sql rename to legacy/apps/yeti/src/queries/sql/supabase/organization-snapshot-1-d/find-grouped-by-organization.sql diff --git a/apps/yeti/src/queries/sql/supabase/organization-snapshot-1-d/find-grouped-by-organization.types.ts b/legacy/apps/yeti/src/queries/sql/supabase/organization-snapshot-1-d/find-grouped-by-organization.types.ts similarity index 100% rename from apps/yeti/src/queries/sql/supabase/organization-snapshot-1-d/find-grouped-by-organization.types.ts rename to legacy/apps/yeti/src/queries/sql/supabase/organization-snapshot-1-d/find-grouped-by-organization.types.ts diff --git a/apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-estimated-production-grouped.sql b/legacy/apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-estimated-production-grouped.sql similarity index 100% rename from apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-estimated-production-grouped.sql rename to legacy/apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-estimated-production-grouped.sql diff --git a/apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-estimated-production-grouped.types.ts b/legacy/apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-estimated-production-grouped.types.ts similarity index 100% rename from apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-estimated-production-grouped.types.ts rename to legacy/apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-estimated-production-grouped.types.ts diff --git a/apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-meter-consumption-grouped.sql b/legacy/apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-meter-consumption-grouped.sql similarity index 100% rename from apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-meter-consumption-grouped.sql rename to legacy/apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-meter-consumption-grouped.sql diff --git a/apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-meter-consumption-grouped.types.ts b/legacy/apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-meter-consumption-grouped.types.ts similarity index 100% rename from apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-meter-consumption-grouped.types.ts rename to legacy/apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-meter-consumption-grouped.types.ts diff --git a/apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-production-grouped.sql b/legacy/apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-production-grouped.sql similarity index 100% rename from apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-production-grouped.sql rename to legacy/apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-production-grouped.sql diff --git a/apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-production-grouped.types.ts b/legacy/apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-production-grouped.types.ts similarity index 100% rename from apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-production-grouped.types.ts rename to legacy/apps/yeti/src/queries/sql/timescale/grid-business-snapshot-1-d/find-production-grouped.types.ts diff --git a/apps/yeti/src/queries/sql/timescale/grid-energy-snapshot-15-min/find-max-mcc-by-date-range.sql b/legacy/apps/yeti/src/queries/sql/timescale/grid-energy-snapshot-15-min/find-max-mcc-by-date-range.sql similarity index 100% rename from apps/yeti/src/queries/sql/timescale/grid-energy-snapshot-15-min/find-max-mcc-by-date-range.sql rename to legacy/apps/yeti/src/queries/sql/timescale/grid-energy-snapshot-15-min/find-max-mcc-by-date-range.sql diff --git a/apps/yeti/src/queries/sql/timescale/grid-energy-snapshot-15-min/find-max-mcc-by-date-range.types.ts b/legacy/apps/yeti/src/queries/sql/timescale/grid-energy-snapshot-15-min/find-max-mcc-by-date-range.types.ts similarity index 100% rename from apps/yeti/src/queries/sql/timescale/grid-energy-snapshot-15-min/find-max-mcc-by-date-range.types.ts rename to legacy/apps/yeti/src/queries/sql/timescale/grid-energy-snapshot-15-min/find-max-mcc-by-date-range.types.ts diff --git a/apps/yeti/src/queries/sql/timescale/meter-snapshot-1-h/find-meter-consumptions.sql b/legacy/apps/yeti/src/queries/sql/timescale/meter-snapshot-1-h/find-meter-consumptions.sql similarity index 100% rename from apps/yeti/src/queries/sql/timescale/meter-snapshot-1-h/find-meter-consumptions.sql rename to legacy/apps/yeti/src/queries/sql/timescale/meter-snapshot-1-h/find-meter-consumptions.sql diff --git a/apps/yeti/src/queries/sql/timescale/meter-snapshot-1-h/find-meter-consumptions.types.ts b/legacy/apps/yeti/src/queries/sql/timescale/meter-snapshot-1-h/find-meter-consumptions.types.ts similarity index 100% rename from apps/yeti/src/queries/sql/timescale/meter-snapshot-1-h/find-meter-consumptions.types.ts rename to legacy/apps/yeti/src/queries/sql/timescale/meter-snapshot-1-h/find-meter-consumptions.types.ts diff --git a/apps/yeti/tsconfig.app.json b/legacy/apps/yeti/tsconfig.app.json similarity index 100% rename from apps/yeti/tsconfig.app.json rename to legacy/apps/yeti/tsconfig.app.json diff --git a/apps/yeti/tsconfig.json b/legacy/apps/yeti/tsconfig.json similarity index 100% rename from apps/yeti/tsconfig.json rename to legacy/apps/yeti/tsconfig.json diff --git a/apps/yeti/tsconfig.spec.json b/legacy/apps/yeti/tsconfig.spec.json similarity index 100% rename from apps/yeti/tsconfig.spec.json rename to legacy/apps/yeti/tsconfig.spec.json diff --git a/apps/yeti/webpack.config.js b/legacy/apps/yeti/webpack.config.js similarity index 100% rename from apps/yeti/webpack.config.js rename to legacy/apps/yeti/webpack.config.js diff --git a/eslint.config.ts b/legacy/eslint.config.ts similarity index 100% rename from eslint.config.ts rename to legacy/eslint.config.ts diff --git a/global.d.ts b/legacy/global.d.ts similarity index 100% rename from global.d.ts rename to legacy/global.d.ts diff --git a/legacy/libs/core/README.md b/legacy/libs/core/README.md new file mode 100644 index 0000000..36a7a9c --- /dev/null +++ b/legacy/libs/core/README.md @@ -0,0 +1,101 @@ +# @core + +Shared NestJS library used by all apps in the monorepo. Provides database connections, shared domain entities and services, third-party API clients, and cross-cutting infrastructure modules. + +**Path alias:** `@core` → `libs/core/src` + +--- + +## What it provides + +- **Database connections** — TypeORM connection to the primary PostgreSQL database (`CoreTypeOrmModule`), raw `pg` pool (`CorePgModule`), and Supabase client (`GlobalSupabaseModule`) +- **Global infrastructure** — HTTP module (`GlobalHttpModule`), structured logging to Loki (`CoreLoggerModule`) +- **Shared domain layer** — TypeORM entities, DTOs, and services for all core mini-grid domain models +- **Third-party clients** — CALIN meter APIs, Victron VRM API, Make.com, Solcast, SendGrid + +--- + +## Exported modules (`libs/core/src/index.ts`) + +| Export | Description | +|--------|-------------| +| `CoreTypeOrmModule` | `TypeOrmModule.forRoot` for the primary PostgreSQL database. Registers all core entities. | +| `GlobalHttpModule` | Global `HttpModule` powered by `@nestjs/axios`. | +| `GlobalSupabaseModule` | Provides `SupabaseService` with a typed Supabase client and `throwSupabaseError` helper. | +| `CoreLoggerModule` | Global `LokiService` — Winston logger with Loki transport and console fallback. | +| `CorePgModule` | Global `PgService` — raw `pg` connection pool with `query` and `getClient` helpers. | + +Apps also import deeply from `@core/modules/...` for domain services and entities. + +--- + +## Shared types + +| File | Description | +|------|-------------| +| `types/core-entity.ts` | `CoreEntity` base class with `id` and `created_at` columns for main DB entities. | +| `types/supabase-types.ts` | Auto-generated and adjusted TypeScript types for the Supabase database schema. | +| `types/device-messaging.ts` | Shared enums for device messaging (e.g. `PhaseEnum`). | +| `types/solcast-type.ts` | Solcast API response types. | + +--- + +## Shared constants + +`libs/core/src/constants.ts` — shared application-wide constants such as `SUPABASE_QUERY_LIMIT` and wallet/organization identifiers. + +--- + +## Domain modules + +All modules live under `libs/core/src/modules/`. Apps import them directly from `@core/modules/`. + +| Module | Description | +|--------|-------------| +| `accounts` | Loads `Account` records with full relations (agent, grid, org, wallet, member). | +| `agents` | Queries agents by grid; loads with account, grid, and wallet relations. | +| `api-keys` | Resolves API keys with associated account relations. | +| `calin` | HTTP client for CALIN v1/v2 and STS token APIs, with retry logic and dev alerts. | +| `connections` | Raw SQL reporting queries — public connections, women impacted, etc. | +| `customers` | Reporting queries for customer groupings. | +| `dcus` | DCU lookups by id, external reference, and system. | +| `energy-tracking` | Queries `grid_business_snapshot_1_d` on TimescaleDB using `time_bucket_gapfill` for energy reporting. | +| `grids` | Grid queries filtered by reporting flags, organization, etc. | +| `issues` | Issue records with meter, customer, and grid relations. | +| `loki` | Winston + Loki transport (backing `CoreLoggerModule`). | +| `make` | Rate-limited POST client to Make.com scenario webhook URLs. | +| `meter-commissionings` | Loads commissioning records by id or session lock. | +| `metering-hardware-imports` | Import records and batch status queries via raw SQL. | +| `metering-hardware-install-sessions` | Install session lookups by id or ids. | +| `meters` | Core meter queries — by external reference, grid, commissioning path. | +| `mppts` | Syncs MPPTs from Victron and Supabase; provides MPPT service used across apps. | +| `notification-parameters` | CRUD stubs for per-grid notification configuration. | +| `orders` | Order/top-up queries with wallet and customer paths. | +| `organizations` | Organization entity. | +| `payouts` | Payout queries by organization or ids. | +| `routers` | Router lookups by grid, external system, and ids. | +| `software-dev-alert` | Sends developer alerts via Make.com (Telegram) and optionally email. | +| `solcast-cache` | DB-backed cache for Solcast API responses, keyed by geo/install parameters. | +| `spending` | Revenue and spending queries via Supabase RPCs and raw SQL. | +| `victron` | Victron VRM API client — JWT auth, rate limiting, site stats, and forecast helpers. | + +**Entity-only modules** (no service logic, provide TypeORM entities): `bank-accounts`, `banks`, `connection-requested-meters`, `directive-*`, `directives`, `loans`, `members`, `meter-credit-transfers`, `notes`, `notifications`, `poles`, `transactions`, `ussd-sessions`, `ussd-session-hops`, `wallets`. + +--- + +## Usage + +Apps import shared modules at the root level in their `app.module.ts`: + +```typescript +import { CoreTypeOrmModule, GlobalSupabaseModule, CoreLoggerModule } from '@core'; +import { CoreMetersModule } from '@core/modules/meters/meters.module'; +``` + +Feature modules then inject services from `@core` as normal NestJS providers. + +--- + +## License + +This project is licensed under the [Mozilla Public License 2.0](https://www.mozilla.org/MPL/2.0/). See the [`LICENSE`](../../LICENSE) file at the repository root. diff --git a/libs/core/jest.config.ts b/legacy/libs/core/jest.config.ts similarity index 100% rename from libs/core/jest.config.ts rename to legacy/libs/core/jest.config.ts diff --git a/libs/core/project.json b/legacy/libs/core/project.json similarity index 100% rename from libs/core/project.json rename to legacy/libs/core/project.json diff --git a/libs/core/seed/1690548742151-init.ts b/legacy/libs/core/seed/1690548742151-init.ts similarity index 100% rename from libs/core/seed/1690548742151-init.ts rename to legacy/libs/core/seed/1690548742151-init.ts diff --git a/legacy/libs/core/src/constants.ts b/legacy/libs/core/src/constants.ts new file mode 100644 index 0000000..8f5a3e4 --- /dev/null +++ b/legacy/libs/core/src/constants.ts @@ -0,0 +1,24 @@ +export const CALIN_API_FAIL = 'CALIN_API_FAIL'; // @TODO :: Delete + +export const NXT_ORG_ID = 2; + +export const BANKING_SYSTEM_WALLET_ID = 968; + +export const SUPABASE_QUERY_LIMIT = 10000; + +// export const GRIDS_USING_METER_INTERACTIONS_FOR_V1 = [ +// 60, // Software Test Grid +// 3, // Ogheye +// 17, // Belel + +// 21, // Akinsolu +// 5, // Demo Community +// 40, // Evbuwa +// 41, // Iguemokhua +// 57, // Iyemero +// 47, // Kosubosu +// 38, // Matari +// 13, // Ogbinbiri +// // 42, // Okpokunou -> Not yet because external testing +// // 46, // Umuolu -> This is V2 and LoRaWAN mixed +// ]; diff --git a/legacy/libs/core/src/index.ts b/legacy/libs/core/src/index.ts new file mode 100644 index 0000000..a7f79d5 --- /dev/null +++ b/legacy/libs/core/src/index.ts @@ -0,0 +1,2 @@ +export { CoreTypeOrmModule } from './modules/core-typeorm.module'; +export { CorePgModule } from './modules/core-pg'; diff --git a/libs/core/src/modules/agents/agents.service.ts b/legacy/libs/core/src/modules/agents/agents.service.ts similarity index 100% rename from libs/core/src/modules/agents/agents.service.ts rename to legacy/libs/core/src/modules/agents/agents.service.ts diff --git a/libs/core/src/modules/agents/entities/agent.entity.ts b/legacy/libs/core/src/modules/agents/entities/agent.entity.ts similarity index 100% rename from libs/core/src/modules/agents/entities/agent.entity.ts rename to legacy/libs/core/src/modules/agents/entities/agent.entity.ts diff --git a/libs/core/src/modules/banks/entities/bank.entity.ts b/legacy/libs/core/src/modules/banks/entities/bank.entity.ts similarity index 69% rename from libs/core/src/modules/banks/entities/bank.entity.ts rename to legacy/libs/core/src/modules/banks/entities/bank.entity.ts index d87e6d7..3b01f57 100644 --- a/libs/core/src/modules/banks/entities/bank.entity.ts +++ b/legacy/libs/core/src/modules/banks/entities/bank.entity.ts @@ -1,7 +1,6 @@ import { Entity, Column, OneToMany } from 'typeorm'; import { CoreEntity } from '@core/types/core-entity'; import { UssdSession } from '@core/modules/ussd-sessions/entities/ussd-session.entity'; -import { BankAccount } from '@core/modules/bank-accounts/entities/bank-account.entity'; @Entity('banks') export class Bank extends CoreEntity { @@ -13,7 +12,4 @@ export class Bank extends CoreEntity { @OneToMany(() => UssdSession, ussdSession => ussdSession.bank) ussd_sessions: UssdSession[]; - - @OneToMany(() => BankAccount, bank_account => bank_account.bank) - bank_accounts: BankAccount[]; } diff --git a/libs/core/src/modules/calin/calin.service.ts b/legacy/libs/core/src/modules/calin/calin.service.ts similarity index 100% rename from libs/core/src/modules/calin/calin.service.ts rename to legacy/libs/core/src/modules/calin/calin.service.ts diff --git a/libs/core/src/modules/connections/connections.module.ts b/legacy/libs/core/src/modules/connections/connections.module.ts similarity index 100% rename from libs/core/src/modules/connections/connections.module.ts rename to legacy/libs/core/src/modules/connections/connections.module.ts diff --git a/libs/core/src/modules/connections/connections.service.ts b/legacy/libs/core/src/modules/connections/connections.service.ts similarity index 100% rename from libs/core/src/modules/connections/connections.service.ts rename to legacy/libs/core/src/modules/connections/connections.service.ts diff --git a/libs/core/src/modules/connections/dto/create-connection.dto.ts b/legacy/libs/core/src/modules/connections/dto/create-connection.dto.ts similarity index 100% rename from libs/core/src/modules/connections/dto/create-connection.dto.ts rename to legacy/libs/core/src/modules/connections/dto/create-connection.dto.ts diff --git a/libs/core/src/modules/connections/entities/connection.entity.ts b/legacy/libs/core/src/modules/connections/entities/connection.entity.ts similarity index 100% rename from libs/core/src/modules/connections/entities/connection.entity.ts rename to legacy/libs/core/src/modules/connections/entities/connection.entity.ts diff --git a/libs/core/src/modules/core-pg.ts b/legacy/libs/core/src/modules/core-pg.ts similarity index 100% rename from libs/core/src/modules/core-pg.ts rename to legacy/libs/core/src/modules/core-pg.ts diff --git a/libs/core/src/modules/core-typeorm.module.ts b/legacy/libs/core/src/modules/core-typeorm.module.ts similarity index 97% rename from libs/core/src/modules/core-typeorm.module.ts rename to legacy/libs/core/src/modules/core-typeorm.module.ts index 457ad4e..0dcfa3c 100644 --- a/libs/core/src/modules/core-typeorm.module.ts +++ b/legacy/libs/core/src/modules/core-typeorm.module.ts @@ -21,7 +21,6 @@ import { Member } from './members/entities/member.entity'; import { UssdSessionHop } from './ussd-session-hops/entities/ussd-session-hop.entity'; import { UssdSession } from './ussd-sessions/entities/ussd-session.entity'; import { Wallet } from './wallets/entities/wallet.entity'; -import { Directive } from './directives/entities/directive.entity'; import { Transaction } from './transactions/entities/transactions.entity'; import { Connection } from './connections/entities/connection.entity'; import { MeteringHardwareInstallSession } from './metering-hardware-install-sessions/entities/metering-hardware-install-session.entity'; diff --git a/libs/core/src/modules/customers/customers.module.ts b/legacy/libs/core/src/modules/customers/customers.module.ts similarity index 100% rename from libs/core/src/modules/customers/customers.module.ts rename to legacy/libs/core/src/modules/customers/customers.module.ts diff --git a/libs/core/src/modules/customers/customers.service.ts b/legacy/libs/core/src/modules/customers/customers.service.ts similarity index 100% rename from libs/core/src/modules/customers/customers.service.ts rename to legacy/libs/core/src/modules/customers/customers.service.ts diff --git a/libs/core/src/modules/customers/entities/customer.entity.ts b/legacy/libs/core/src/modules/customers/entities/customer.entity.ts similarity index 100% rename from libs/core/src/modules/customers/entities/customer.entity.ts rename to legacy/libs/core/src/modules/customers/entities/customer.entity.ts diff --git a/libs/core/src/modules/dcus/dcus.module.ts b/legacy/libs/core/src/modules/dcus/dcus.module.ts similarity index 100% rename from libs/core/src/modules/dcus/dcus.module.ts rename to legacy/libs/core/src/modules/dcus/dcus.module.ts diff --git a/libs/core/src/modules/dcus/dcus.service.ts b/legacy/libs/core/src/modules/dcus/dcus.service.ts similarity index 100% rename from libs/core/src/modules/dcus/dcus.service.ts rename to legacy/libs/core/src/modules/dcus/dcus.service.ts diff --git a/libs/core/src/modules/dcus/dto/create-dcu.input.ts b/legacy/libs/core/src/modules/dcus/dto/create-dcu.input.ts similarity index 100% rename from libs/core/src/modules/dcus/dto/create-dcu.input.ts rename to legacy/libs/core/src/modules/dcus/dto/create-dcu.input.ts diff --git a/libs/core/src/modules/dcus/dto/update-dcu.input.ts b/legacy/libs/core/src/modules/dcus/dto/update-dcu.input.ts similarity index 100% rename from libs/core/src/modules/dcus/dto/update-dcu.input.ts rename to legacy/libs/core/src/modules/dcus/dto/update-dcu.input.ts diff --git a/libs/core/src/modules/dcus/entities/dcu.entity.ts b/legacy/libs/core/src/modules/dcus/entities/dcu.entity.ts similarity index 100% rename from libs/core/src/modules/dcus/entities/dcu.entity.ts rename to legacy/libs/core/src/modules/dcus/entities/dcu.entity.ts diff --git a/libs/core/src/modules/directive-batch-executions/entities/directive-batch-execution.entity.ts b/legacy/libs/core/src/modules/directive-batch-executions/entities/directive-batch-execution.entity.ts similarity index 83% rename from libs/core/src/modules/directive-batch-executions/entities/directive-batch-execution.entity.ts rename to legacy/libs/core/src/modules/directive-batch-executions/entities/directive-batch-execution.entity.ts index 4f5ef03..533c656 100644 --- a/libs/core/src/modules/directive-batch-executions/entities/directive-batch-execution.entity.ts +++ b/legacy/libs/core/src/modules/directive-batch-executions/entities/directive-batch-execution.entity.ts @@ -1,4 +1,3 @@ -import { Directive } from '@core/modules/directives/entities/directive.entity'; import { CoreEntity } from '@core/types/core-entity'; import { DirectiveBatch } from '@core/modules/directive-batches/entities/directive-batch.entity'; import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from 'typeorm'; @@ -7,9 +6,6 @@ import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from 'typeorm'; @Entity('directive_batch_executions') export class DirectiveBatchExecution extends CoreEntity { - @OneToMany(() => Directive, directive => directive.directive_batch_execution) - directives: Directive[]; - @ManyToOne(() => DirectiveBatch, directive_batch => directive_batch.directive_batch_executions) @JoinColumn({ name: 'directive_batch_id' }) directive_batch: DirectiveBatch; diff --git a/libs/core/src/modules/directive-batches/entities/directive-batch.entity.ts b/legacy/libs/core/src/modules/directive-batches/entities/directive-batch.entity.ts similarity index 100% rename from libs/core/src/modules/directive-batches/entities/directive-batch.entity.ts rename to legacy/libs/core/src/modules/directive-batches/entities/directive-batch.entity.ts diff --git a/libs/core/src/modules/energy-tracking/energy-tracking.module.ts b/legacy/libs/core/src/modules/energy-tracking/energy-tracking.module.ts similarity index 100% rename from libs/core/src/modules/energy-tracking/energy-tracking.module.ts rename to legacy/libs/core/src/modules/energy-tracking/energy-tracking.module.ts diff --git a/libs/core/src/modules/energy-tracking/energy-tracking.service.ts b/legacy/libs/core/src/modules/energy-tracking/energy-tracking.service.ts similarity index 100% rename from libs/core/src/modules/energy-tracking/energy-tracking.service.ts rename to legacy/libs/core/src/modules/energy-tracking/energy-tracking.service.ts diff --git a/libs/core/src/modules/grids/dto/create-grid.input.ts b/legacy/libs/core/src/modules/grids/dto/create-grid.input.ts similarity index 100% rename from libs/core/src/modules/grids/dto/create-grid.input.ts rename to legacy/libs/core/src/modules/grids/dto/create-grid.input.ts diff --git a/libs/core/src/modules/grids/dto/update-grid.input.ts b/legacy/libs/core/src/modules/grids/dto/update-grid.input.ts similarity index 100% rename from libs/core/src/modules/grids/dto/update-grid.input.ts rename to legacy/libs/core/src/modules/grids/dto/update-grid.input.ts diff --git a/libs/core/src/modules/grids/entities/grid.entity.ts b/legacy/libs/core/src/modules/grids/entities/grid.entity.ts similarity index 100% rename from libs/core/src/modules/grids/entities/grid.entity.ts rename to legacy/libs/core/src/modules/grids/entities/grid.entity.ts diff --git a/libs/core/src/modules/grids/grids.module.ts b/legacy/libs/core/src/modules/grids/grids.module.ts similarity index 100% rename from libs/core/src/modules/grids/grids.module.ts rename to legacy/libs/core/src/modules/grids/grids.module.ts diff --git a/libs/core/src/modules/grids/grids.service.ts b/legacy/libs/core/src/modules/grids/grids.service.ts similarity index 92% rename from libs/core/src/modules/grids/grids.service.ts rename to legacy/libs/core/src/modules/grids/grids.service.ts index e9e5159..e587e26 100644 --- a/libs/core/src/modules/grids/grids.service.ts +++ b/legacy/libs/core/src/modules/grids/grids.service.ts @@ -4,6 +4,11 @@ import { Repository } from 'typeorm'; import { Grid } from './entities/grid.entity'; // import { ExternalSystemEnum } from '@core/types/supabase-types'; +/** + * 002d Task 10/11 — not ported to OSS Foundation Nest. Grid table stays Foundation data; + * new hosts query at the call site. Retain until Metering / legacy absorption completes. + * findAll / findByOrganizationId* had no in-repo callers; findOne kept for legacy injectors. + */ @Injectable() export class GridsService { constructor( diff --git a/libs/core/src/modules/issues/dto/create-issue.input.ts b/legacy/libs/core/src/modules/issues/dto/create-issue.input.ts similarity index 100% rename from libs/core/src/modules/issues/dto/create-issue.input.ts rename to legacy/libs/core/src/modules/issues/dto/create-issue.input.ts diff --git a/libs/core/src/modules/issues/dto/update-issue.input.ts b/legacy/libs/core/src/modules/issues/dto/update-issue.input.ts similarity index 100% rename from libs/core/src/modules/issues/dto/update-issue.input.ts rename to legacy/libs/core/src/modules/issues/dto/update-issue.input.ts diff --git a/libs/core/src/modules/issues/entities/issue.entity.ts b/legacy/libs/core/src/modules/issues/entities/issue.entity.ts similarity index 100% rename from libs/core/src/modules/issues/entities/issue.entity.ts rename to legacy/libs/core/src/modules/issues/entities/issue.entity.ts diff --git a/libs/core/src/modules/issues/issues.module.ts b/legacy/libs/core/src/modules/issues/issues.module.ts similarity index 100% rename from libs/core/src/modules/issues/issues.module.ts rename to legacy/libs/core/src/modules/issues/issues.module.ts diff --git a/libs/core/src/modules/issues/issues.service.ts b/legacy/libs/core/src/modules/issues/issues.service.ts similarity index 100% rename from libs/core/src/modules/issues/issues.service.ts rename to legacy/libs/core/src/modules/issues/issues.service.ts diff --git a/libs/core/src/modules/loans/loans.service.ts b/legacy/libs/core/src/modules/loans/loans.service.ts similarity index 100% rename from libs/core/src/modules/loans/loans.service.ts rename to legacy/libs/core/src/modules/loans/loans.service.ts diff --git a/libs/core/src/modules/make/make.service.ts b/legacy/libs/core/src/modules/make/make.service.ts similarity index 100% rename from libs/core/src/modules/make/make.service.ts rename to legacy/libs/core/src/modules/make/make.service.ts diff --git a/libs/core/src/modules/meter-commissionings/dto/create-meter-commissioning.input.ts b/legacy/libs/core/src/modules/meter-commissionings/dto/create-meter-commissioning.input.ts similarity index 100% rename from libs/core/src/modules/meter-commissionings/dto/create-meter-commissioning.input.ts rename to legacy/libs/core/src/modules/meter-commissionings/dto/create-meter-commissioning.input.ts diff --git a/libs/core/src/modules/meter-commissionings/dto/update-meter-commissioning.input.ts b/legacy/libs/core/src/modules/meter-commissionings/dto/update-meter-commissioning.input.ts similarity index 100% rename from libs/core/src/modules/meter-commissionings/dto/update-meter-commissioning.input.ts rename to legacy/libs/core/src/modules/meter-commissionings/dto/update-meter-commissioning.input.ts diff --git a/libs/core/src/modules/meter-commissionings/entities/meter-commissioning.entity.ts b/legacy/libs/core/src/modules/meter-commissionings/entities/meter-commissioning.entity.ts similarity index 90% rename from libs/core/src/modules/meter-commissionings/entities/meter-commissioning.entity.ts rename to legacy/libs/core/src/modules/meter-commissionings/entities/meter-commissioning.entity.ts index 4a8d7d2..8ee2b4c 100644 --- a/libs/core/src/modules/meter-commissionings/entities/meter-commissioning.entity.ts +++ b/legacy/libs/core/src/modules/meter-commissionings/entities/meter-commissioning.entity.ts @@ -1,14 +1,10 @@ import { CoreEntity } from '@core/types/core-entity'; import { Column, Entity, JoinColumn, ManyToOne, OneToMany, OneToOne } from 'typeorm'; -import { Directive } from '@core/modules/directives/entities/directive.entity'; import { MeteringHardwareInstallSession } from '@core/modules/metering-hardware-install-sessions/entities/metering-hardware-install-session.entity'; import { MeterCommissioningStatusEnum } from '@core/types/supabase-types'; @Entity('meter_commissionings') export class MeterCommissioning extends CoreEntity { - @OneToMany(() => Directive, directive => directive.meter_commissioning) - directives: Directive[]; - // @Column('enum', { enum: MeterCommissioningStatusEnum, default: MeterCommissioningStatusEnum.PROCESSING }) @Column({ type: 'varchar' }) meter_commissioning_status: MeterCommissioningStatusEnum; diff --git a/libs/core/src/modules/meter-commissionings/meter-commissionings.service.ts b/legacy/libs/core/src/modules/meter-commissionings/meter-commissionings.service.ts similarity index 100% rename from libs/core/src/modules/meter-commissionings/meter-commissionings.service.ts rename to legacy/libs/core/src/modules/meter-commissionings/meter-commissionings.service.ts diff --git a/libs/core/src/modules/metering-hardware-imports/dto/create-metering-hardware-import.input.ts b/legacy/libs/core/src/modules/metering-hardware-imports/dto/create-metering-hardware-import.input.ts similarity index 100% rename from libs/core/src/modules/metering-hardware-imports/dto/create-metering-hardware-import.input.ts rename to legacy/libs/core/src/modules/metering-hardware-imports/dto/create-metering-hardware-import.input.ts diff --git a/libs/core/src/modules/metering-hardware-imports/dto/update-metering-hardware-import.input.ts b/legacy/libs/core/src/modules/metering-hardware-imports/dto/update-metering-hardware-import.input.ts similarity index 100% rename from libs/core/src/modules/metering-hardware-imports/dto/update-metering-hardware-import.input.ts rename to legacy/libs/core/src/modules/metering-hardware-imports/dto/update-metering-hardware-import.input.ts diff --git a/libs/core/src/modules/metering-hardware-imports/entities/metering-hardware-import.entity.ts b/legacy/libs/core/src/modules/metering-hardware-imports/entities/metering-hardware-import.entity.ts similarity index 100% rename from libs/core/src/modules/metering-hardware-imports/entities/metering-hardware-import.entity.ts rename to legacy/libs/core/src/modules/metering-hardware-imports/entities/metering-hardware-import.entity.ts diff --git a/libs/core/src/modules/metering-hardware-imports/metering-hardware-imports.service.ts b/legacy/libs/core/src/modules/metering-hardware-imports/metering-hardware-imports.service.ts similarity index 100% rename from libs/core/src/modules/metering-hardware-imports/metering-hardware-imports.service.ts rename to legacy/libs/core/src/modules/metering-hardware-imports/metering-hardware-imports.service.ts diff --git a/libs/core/src/modules/metering-hardware-install-sessions/dto/create-metering-hardware-install-session.input.ts b/legacy/libs/core/src/modules/metering-hardware-install-sessions/dto/create-metering-hardware-install-session.input.ts similarity index 100% rename from libs/core/src/modules/metering-hardware-install-sessions/dto/create-metering-hardware-install-session.input.ts rename to legacy/libs/core/src/modules/metering-hardware-install-sessions/dto/create-metering-hardware-install-session.input.ts diff --git a/libs/core/src/modules/metering-hardware-install-sessions/dto/update-metering-hardware-install-session.input.ts b/legacy/libs/core/src/modules/metering-hardware-install-sessions/dto/update-metering-hardware-install-session.input.ts similarity index 100% rename from libs/core/src/modules/metering-hardware-install-sessions/dto/update-metering-hardware-install-session.input.ts rename to legacy/libs/core/src/modules/metering-hardware-install-sessions/dto/update-metering-hardware-install-session.input.ts diff --git a/libs/core/src/modules/metering-hardware-install-sessions/entities/metering-hardware-install-session.entity.ts b/legacy/libs/core/src/modules/metering-hardware-install-sessions/entities/metering-hardware-install-session.entity.ts similarity index 100% rename from libs/core/src/modules/metering-hardware-install-sessions/entities/metering-hardware-install-session.entity.ts rename to legacy/libs/core/src/modules/metering-hardware-install-sessions/entities/metering-hardware-install-session.entity.ts diff --git a/libs/core/src/modules/metering-hardware-install-sessions/metering-hardware-install-sessions.service.ts b/legacy/libs/core/src/modules/metering-hardware-install-sessions/metering-hardware-install-sessions.service.ts similarity index 100% rename from libs/core/src/modules/metering-hardware-install-sessions/metering-hardware-install-sessions.service.ts rename to legacy/libs/core/src/modules/metering-hardware-install-sessions/metering-hardware-install-sessions.service.ts diff --git a/libs/core/src/modules/meters/dto/create-meter.input.ts b/legacy/libs/core/src/modules/meters/dto/create-meter.input.ts similarity index 88% rename from libs/core/src/modules/meters/dto/create-meter.input.ts rename to legacy/libs/core/src/modules/meters/dto/create-meter.input.ts index abb611a..60fa683 100644 --- a/libs/core/src/modules/meters/dto/create-meter.input.ts +++ b/legacy/libs/core/src/modules/meters/dto/create-meter.input.ts @@ -1,7 +1,7 @@ import { Dcu } from '@core/modules/dcus/entities/dcu.entity'; import { Issue } from '@core/modules/issues/entities/issue.entity'; import { Connection } from '@core/modules/connections/entities/connection.entity'; -import { CommunicationProtocolEnum, DirectiveSpecialStatusEnum, ExternalSystemEnum, MeterPhaseEnum, MeterTypeEnum } from '@core/types/supabase-types'; +import { CommunicationProtocolEnum, ExternalSystemEnum, MeterPhaseEnum, MeterTypeEnum } from '@core/types/supabase-types'; export class CreateMeterInput { @@ -64,7 +64,6 @@ export class CreateMeterInput { is_starred?: boolean; version?: string; - current_special_status?: DirectiveSpecialStatusEnum; communication_protocol?: CommunicationProtocolEnum; is_cabin_meter?: boolean; diff --git a/libs/core/src/modules/meters/dto/update-meter.input.ts b/legacy/libs/core/src/modules/meters/dto/update-meter.input.ts similarity index 100% rename from libs/core/src/modules/meters/dto/update-meter.input.ts rename to legacy/libs/core/src/modules/meters/dto/update-meter.input.ts diff --git a/libs/core/src/modules/meters/entities/meter.entity.ts b/legacy/libs/core/src/modules/meters/entities/meter.entity.ts similarity index 87% rename from libs/core/src/modules/meters/entities/meter.entity.ts rename to legacy/libs/core/src/modules/meters/entities/meter.entity.ts index 14bebe0..fb74056 100644 --- a/libs/core/src/modules/meters/entities/meter.entity.ts +++ b/legacy/libs/core/src/modules/meters/entities/meter.entity.ts @@ -2,7 +2,6 @@ import { Entity, Column, JoinColumn, ManyToOne, OneToMany, Index, OneToOne, Dele import { CoreEntity } from '@core/types/core-entity'; import { Dcu } from '@core/modules/dcus/entities/dcu.entity'; import { Pole } from '@core/modules/poles/entities/pole.entity'; -import { Directive } from '@core/modules/directives/entities/directive.entity'; import { UssdSession } from '@core/modules/ussd-sessions/entities/ussd-session.entity'; import { Connection } from '@core/modules/connections/entities/connection.entity'; import { Wallet } from '@core/modules/wallets/entities/wallet.entity'; @@ -10,9 +9,8 @@ import { Issue } from '@core/modules/issues/entities/issue.entity'; // import { Audit } from '@core/modules/audits/entities/audit.entity'; import { MeteringHardwareInstallSession } from '@core/modules/metering-hardware-install-sessions/entities/metering-hardware-install-session.entity'; import { Note } from '@core/modules/notes/entities/note.entity'; -import { MeterCreditTransfer } from '@core/modules/meter-credit-transfers/entities/meter-credit-transfer.entity'; -import { CommunicationProtocolEnum, DirectiveSpecialStatusEnum, ExternalSystemEnum, MeterPhaseEnum, MeterTypeEnum } from '@core/types/supabase-types'; +import { CommunicationProtocolEnum, ExternalSystemEnum, MeterPhaseEnum, MeterTypeEnum } from '@core/types/supabase-types'; @Entity('meters') @Index([ 'external_reference', 'external_system' ], { unique: true }) //there cannot be more than one external reference from the same provider @@ -102,18 +100,9 @@ export class Meter extends CoreEntity { @Column('bool', { default: false }) is_starred?: boolean; - @OneToMany(() => Directive, directive => directive.meter) - directives?: Directive[]; - @OneToMany(() => MeteringHardwareInstallSession, install_session => install_session.meter) meter_install_sessions?: MeteringHardwareInstallSession[]; - @OneToMany(() => MeterCreditTransfer, meter_credit_transfer => meter_credit_transfer.sender_meter) - sender_meter_credit_transfers?: MeterCreditTransfer[]; - - @OneToMany(() => MeterCreditTransfer, meter_credit_transfer => meter_credit_transfer.receiver_meter) - receiver_meter_credit_transfers?: MeterCreditTransfer[]; - @OneToMany(() => Issue, issue => issue.meter) issues?: Issue[]; @@ -207,11 +196,6 @@ export class Meter extends CoreEntity { @Column('int', { default: 200 }) power_limit_hps_mode?: number; - // @Column('enum', { enum: DirectiveSpecialStatusEnum, nullable: true }) - @Column({ type: 'varchar' }) - current_special_status?: DirectiveSpecialStatusEnum; - - // @Column('enum', { enum: CommunicationProtocolEnum, nullable: true }) @Column({ type: 'varchar' }) communication_protocol: CommunicationProtocolEnum; diff --git a/libs/core/src/modules/meters/meters.module.ts b/legacy/libs/core/src/modules/meters/meters.module.ts similarity index 100% rename from libs/core/src/modules/meters/meters.module.ts rename to legacy/libs/core/src/modules/meters/meters.module.ts diff --git a/libs/core/src/modules/meters/meters.service.ts b/legacy/libs/core/src/modules/meters/meters.service.ts similarity index 100% rename from libs/core/src/modules/meters/meters.service.ts rename to legacy/libs/core/src/modules/meters/meters.service.ts diff --git a/libs/core/src/modules/mppts/entities/mppt.entity.ts b/legacy/libs/core/src/modules/mppts/entities/mppt.entity.ts similarity index 100% rename from libs/core/src/modules/mppts/entities/mppt.entity.ts rename to legacy/libs/core/src/modules/mppts/entities/mppt.entity.ts diff --git a/libs/core/src/modules/mppts/mppts.service.ts b/legacy/libs/core/src/modules/mppts/mppts.service.ts similarity index 100% rename from libs/core/src/modules/mppts/mppts.service.ts rename to legacy/libs/core/src/modules/mppts/mppts.service.ts diff --git a/libs/core/src/modules/notes/entities/note.entity.ts b/legacy/libs/core/src/modules/notes/entities/note.entity.ts similarity index 100% rename from libs/core/src/modules/notes/entities/note.entity.ts rename to legacy/libs/core/src/modules/notes/entities/note.entity.ts diff --git a/libs/core/src/modules/notification-parameters/dto/create-notification-parameter.input.ts b/legacy/libs/core/src/modules/notification-parameters/dto/create-notification-parameter.input.ts similarity index 100% rename from libs/core/src/modules/notification-parameters/dto/create-notification-parameter.input.ts rename to legacy/libs/core/src/modules/notification-parameters/dto/create-notification-parameter.input.ts diff --git a/libs/core/src/modules/notification-parameters/dto/update-notification-parameter.input.ts b/legacy/libs/core/src/modules/notification-parameters/dto/update-notification-parameter.input.ts similarity index 100% rename from libs/core/src/modules/notification-parameters/dto/update-notification-parameter.input.ts rename to legacy/libs/core/src/modules/notification-parameters/dto/update-notification-parameter.input.ts diff --git a/libs/core/src/modules/notification-parameters/entities/notification-parameter.entity.ts b/legacy/libs/core/src/modules/notification-parameters/entities/notification-parameter.entity.ts similarity index 100% rename from libs/core/src/modules/notification-parameters/entities/notification-parameter.entity.ts rename to legacy/libs/core/src/modules/notification-parameters/entities/notification-parameter.entity.ts diff --git a/libs/core/src/modules/notification-parameters/notification-parameters.service.ts b/legacy/libs/core/src/modules/notification-parameters/notification-parameters.service.ts similarity index 100% rename from libs/core/src/modules/notification-parameters/notification-parameters.service.ts rename to legacy/libs/core/src/modules/notification-parameters/notification-parameters.service.ts diff --git a/libs/core/src/modules/notifications/dto/create-notification.input.ts b/legacy/libs/core/src/modules/notifications/dto/create-notification.input.ts similarity index 100% rename from libs/core/src/modules/notifications/dto/create-notification.input.ts rename to legacy/libs/core/src/modules/notifications/dto/create-notification.input.ts diff --git a/libs/core/src/modules/notifications/dto/update-notification.input.ts b/legacy/libs/core/src/modules/notifications/dto/update-notification.input.ts similarity index 100% rename from libs/core/src/modules/notifications/dto/update-notification.input.ts rename to legacy/libs/core/src/modules/notifications/dto/update-notification.input.ts diff --git a/libs/core/src/modules/notifications/entities/notification.entity.ts b/legacy/libs/core/src/modules/notifications/entities/notification.entity.ts similarity index 100% rename from libs/core/src/modules/notifications/entities/notification.entity.ts rename to legacy/libs/core/src/modules/notifications/entities/notification.entity.ts diff --git a/libs/core/src/modules/orders/dto/create-order.input.ts b/legacy/libs/core/src/modules/orders/dto/create-order.input.ts similarity index 79% rename from libs/core/src/modules/orders/dto/create-order.input.ts rename to legacy/libs/core/src/modules/orders/dto/create-order.input.ts index 58120dc..2617b59 100644 --- a/libs/core/src/modules/orders/dto/create-order.input.ts +++ b/legacy/libs/core/src/modules/orders/dto/create-order.input.ts @@ -2,8 +2,6 @@ import { Account } from '@core/modules/accounts/entities/account.entity'; import { UssdSession } from '@core/modules/ussd-sessions/entities/ussd-session.entity'; import { Wallet } from '@core/modules/wallets/entities/wallet.entity'; import { Connection } from '@core/modules/connections/entities/connection.entity'; -import { Directive } from '@core/modules/directives/entities/directive.entity'; -import { MeterCreditTransfer } from '@core/modules/meter-credit-transfers/entities/meter-credit-transfer.entity'; import { CurrencyEnum, MeterTypeEnum, OrderActorTypeEnum, OrderStatusEnum, PaymentChannelEnum, PaymentMethodEnum } from '@core/types/supabase-types'; export class CreateOrderInput { @@ -39,12 +37,6 @@ export class CreateOrderInput { payment_channel?: PaymentChannelEnum; - directive?: Directive; - - directive_id?: number; - - meter_credit_transfer?: MeterCreditTransfer; - meta_sender_type?: OrderActorTypeEnum; meta_receiver_type?: OrderActorTypeEnum; diff --git a/libs/core/src/modules/orders/dto/update-order.input.ts b/legacy/libs/core/src/modules/orders/dto/update-order.input.ts similarity index 68% rename from libs/core/src/modules/orders/dto/update-order.input.ts rename to legacy/libs/core/src/modules/orders/dto/update-order.input.ts index 2a75327..fc7d16f 100644 --- a/libs/core/src/modules/orders/dto/update-order.input.ts +++ b/legacy/libs/core/src/modules/orders/dto/update-order.input.ts @@ -1,15 +1,10 @@ import { MeterTypeEnum, OrderStatusEnum, PaymentMethodEnum } from '@core/types/supabase-types'; -import { Directive } from '@core/modules/directives/entities/directive.entity'; export class UpdateOrderInput { id?: number; order_status?: OrderStatusEnum; - directive?: Directive; - - directive_id?: number; - tariff?: number; tariff_type?: MeterTypeEnum; diff --git a/libs/core/src/modules/orders/entities/order.entity.ts b/legacy/libs/core/src/modules/orders/entities/order.entity.ts similarity index 90% rename from libs/core/src/modules/orders/entities/order.entity.ts rename to legacy/libs/core/src/modules/orders/entities/order.entity.ts index a376861..9f615e7 100644 --- a/libs/core/src/modules/orders/entities/order.entity.ts +++ b/legacy/libs/core/src/modules/orders/entities/order.entity.ts @@ -4,9 +4,7 @@ import { CoreEntity } from '@core/types/core-entity'; import { Wallet } from '@core/modules/wallets/entities/wallet.entity'; import { Transaction } from '@core/modules/transactions/entities/transactions.entity'; import { UssdSession } from '@core/modules/ussd-sessions/entities/ussd-session.entity'; -import { Directive } from '@core/modules/directives/entities/directive.entity'; import { Account } from '@core/modules/accounts/entities/account.entity'; -import { MeterCreditTransfer } from '@core/modules/meter-credit-transfers/entities/meter-credit-transfer.entity'; import { Grid } from '@core/modules/grids/entities/grid.entity'; import { AccountTypeEnum, CurrencyEnum, ExternalSystemEnum, MeterTypeEnum, OrderActorTypeEnum, OrderStatusEnum, OrderTypeEnum, PaymentChannelEnum, PaymentMethodEnum } from '@core/types/supabase-types'; @@ -78,13 +76,6 @@ export class Order extends CoreEntity { @OneToMany(() => Transaction, transaction => transaction.order) transactions?: Transaction[]; - @OneToMany(() => Directive, directive => directive.order) - directives?: Directive[]; - - @OneToOne(() => Directive) - @JoinColumn({ name: 'directive_id' }) - directive?: Directive; - @Column('int', { nullable: true }) directive_id?: number; @@ -95,10 +86,6 @@ export class Order extends CoreEntity { @Column('int', { nullable: true }) meter_credit_transfer_id?: number; - @OneToOne(() => MeterCreditTransfer) - @JoinColumn({ name: 'meter_credit_transfer_id' }) - meter_credit_transfer?: MeterCreditTransfer; - // Historical fields are fields that represent a historical link between two entities. // For example, in this case a meter might have been uninstalled from a grid, so by the time // we run the query, it might be that the order cannot be traced back to the grid anymore diff --git a/libs/core/src/modules/orders/orders.service.ts b/legacy/libs/core/src/modules/orders/orders.service.ts similarity index 100% rename from libs/core/src/modules/orders/orders.service.ts rename to legacy/libs/core/src/modules/orders/orders.service.ts diff --git a/libs/core/src/modules/payouts/dto/create-payout.input.ts b/legacy/libs/core/src/modules/payouts/dto/create-payout.input.ts similarity index 100% rename from libs/core/src/modules/payouts/dto/create-payout.input.ts rename to legacy/libs/core/src/modules/payouts/dto/create-payout.input.ts diff --git a/libs/core/src/modules/payouts/dto/generate-payout.dto.ts b/legacy/libs/core/src/modules/payouts/dto/generate-payout.dto.ts similarity index 100% rename from libs/core/src/modules/payouts/dto/generate-payout.dto.ts rename to legacy/libs/core/src/modules/payouts/dto/generate-payout.dto.ts diff --git a/libs/core/src/modules/payouts/dto/update-payout.input.ts b/legacy/libs/core/src/modules/payouts/dto/update-payout.input.ts similarity index 100% rename from libs/core/src/modules/payouts/dto/update-payout.input.ts rename to legacy/libs/core/src/modules/payouts/dto/update-payout.input.ts diff --git a/libs/core/src/modules/payouts/entities/payout.entity.ts b/legacy/libs/core/src/modules/payouts/entities/payout.entity.ts similarity index 100% rename from libs/core/src/modules/payouts/entities/payout.entity.ts rename to legacy/libs/core/src/modules/payouts/entities/payout.entity.ts diff --git a/libs/core/src/modules/payouts/payouts.service.ts b/legacy/libs/core/src/modules/payouts/payouts.service.ts similarity index 100% rename from libs/core/src/modules/payouts/payouts.service.ts rename to legacy/libs/core/src/modules/payouts/payouts.service.ts diff --git a/libs/core/src/modules/poles/entities/pole.entity.ts b/legacy/libs/core/src/modules/poles/entities/pole.entity.ts similarity index 100% rename from libs/core/src/modules/poles/entities/pole.entity.ts rename to legacy/libs/core/src/modules/poles/entities/pole.entity.ts diff --git a/libs/core/src/modules/routers/dto/create-router.input.ts b/legacy/libs/core/src/modules/routers/dto/create-router.input.ts similarity index 100% rename from libs/core/src/modules/routers/dto/create-router.input.ts rename to legacy/libs/core/src/modules/routers/dto/create-router.input.ts diff --git a/libs/core/src/modules/routers/dto/find-all-routers.input.ts b/legacy/libs/core/src/modules/routers/dto/find-all-routers.input.ts similarity index 100% rename from libs/core/src/modules/routers/dto/find-all-routers.input.ts rename to legacy/libs/core/src/modules/routers/dto/find-all-routers.input.ts diff --git a/libs/core/src/modules/routers/dto/update-router.input.ts b/legacy/libs/core/src/modules/routers/dto/update-router.input.ts similarity index 100% rename from libs/core/src/modules/routers/dto/update-router.input.ts rename to legacy/libs/core/src/modules/routers/dto/update-router.input.ts diff --git a/libs/core/src/modules/routers/entities/router.entity.ts b/legacy/libs/core/src/modules/routers/entities/router.entity.ts similarity index 100% rename from libs/core/src/modules/routers/entities/router.entity.ts rename to legacy/libs/core/src/modules/routers/entities/router.entity.ts diff --git a/libs/core/src/modules/routers/routers.service.ts b/legacy/libs/core/src/modules/routers/routers.service.ts similarity index 100% rename from libs/core/src/modules/routers/routers.service.ts rename to legacy/libs/core/src/modules/routers/routers.service.ts diff --git a/libs/core/src/modules/software-dev-alert/software-dev-alert.module.ts b/legacy/libs/core/src/modules/software-dev-alert/software-dev-alert.module.ts similarity index 100% rename from libs/core/src/modules/software-dev-alert/software-dev-alert.module.ts rename to legacy/libs/core/src/modules/software-dev-alert/software-dev-alert.module.ts diff --git a/libs/core/src/modules/software-dev-alert/software-dev-alert.service.ts b/legacy/libs/core/src/modules/software-dev-alert/software-dev-alert.service.ts similarity index 100% rename from libs/core/src/modules/software-dev-alert/software-dev-alert.service.ts rename to legacy/libs/core/src/modules/software-dev-alert/software-dev-alert.service.ts diff --git a/libs/core/src/modules/solcast-cache/dto/create-solcast-cache.dto.ts b/legacy/libs/core/src/modules/solcast-cache/dto/create-solcast-cache.dto.ts similarity index 100% rename from libs/core/src/modules/solcast-cache/dto/create-solcast-cache.dto.ts rename to legacy/libs/core/src/modules/solcast-cache/dto/create-solcast-cache.dto.ts diff --git a/libs/core/src/modules/solcast-cache/entities/solcast-cache.entity.ts b/legacy/libs/core/src/modules/solcast-cache/entities/solcast-cache.entity.ts similarity index 100% rename from libs/core/src/modules/solcast-cache/entities/solcast-cache.entity.ts rename to legacy/libs/core/src/modules/solcast-cache/entities/solcast-cache.entity.ts diff --git a/libs/core/src/modules/solcast-cache/solcast-cache.service.ts b/legacy/libs/core/src/modules/solcast-cache/solcast-cache.service.ts similarity index 100% rename from libs/core/src/modules/solcast-cache/solcast-cache.service.ts rename to legacy/libs/core/src/modules/solcast-cache/solcast-cache.service.ts diff --git a/libs/core/src/modules/spending/spending.module.ts b/legacy/libs/core/src/modules/spending/spending.module.ts similarity index 100% rename from libs/core/src/modules/spending/spending.module.ts rename to legacy/libs/core/src/modules/spending/spending.module.ts diff --git a/libs/core/src/modules/spending/spending.service.ts b/legacy/libs/core/src/modules/spending/spending.service.ts similarity index 100% rename from libs/core/src/modules/spending/spending.service.ts rename to legacy/libs/core/src/modules/spending/spending.service.ts diff --git a/libs/core/src/modules/transactions/entities/transactions.entity.ts b/legacy/libs/core/src/modules/transactions/entities/transactions.entity.ts similarity index 100% rename from libs/core/src/modules/transactions/entities/transactions.entity.ts rename to legacy/libs/core/src/modules/transactions/entities/transactions.entity.ts diff --git a/libs/core/src/modules/ussd-session-hops/entities/ussd-session-hop.entity.ts b/legacy/libs/core/src/modules/ussd-session-hops/entities/ussd-session-hop.entity.ts similarity index 100% rename from libs/core/src/modules/ussd-session-hops/entities/ussd-session-hop.entity.ts rename to legacy/libs/core/src/modules/ussd-session-hops/entities/ussd-session-hop.entity.ts diff --git a/libs/core/src/modules/ussd-sessions/entities/ussd-session.entity.ts b/legacy/libs/core/src/modules/ussd-sessions/entities/ussd-session.entity.ts similarity index 100% rename from libs/core/src/modules/ussd-sessions/entities/ussd-session.entity.ts rename to legacy/libs/core/src/modules/ussd-sessions/entities/ussd-session.entity.ts diff --git a/libs/core/src/modules/victron/victron.module.ts b/legacy/libs/core/src/modules/victron/victron.module.ts similarity index 100% rename from libs/core/src/modules/victron/victron.module.ts rename to legacy/libs/core/src/modules/victron/victron.module.ts diff --git a/libs/core/src/modules/victron/victron.service.ts b/legacy/libs/core/src/modules/victron/victron.service.ts similarity index 100% rename from libs/core/src/modules/victron/victron.service.ts rename to legacy/libs/core/src/modules/victron/victron.service.ts diff --git a/libs/core/src/modules/victron/victron.types.ts b/legacy/libs/core/src/modules/victron/victron.types.ts similarity index 100% rename from libs/core/src/modules/victron/victron.types.ts rename to legacy/libs/core/src/modules/victron/victron.types.ts diff --git a/libs/core/src/modules/wallets/entities/wallet.entity.ts b/legacy/libs/core/src/modules/wallets/entities/wallet.entity.ts similarity index 100% rename from libs/core/src/modules/wallets/entities/wallet.entity.ts rename to legacy/libs/core/src/modules/wallets/entities/wallet.entity.ts diff --git a/libs/core/src/types/core-entity.ts b/legacy/libs/core/src/types/core-entity.ts similarity index 100% rename from libs/core/src/types/core-entity.ts rename to legacy/libs/core/src/types/core-entity.ts diff --git a/libs/core/src/types/device-messaging.ts b/legacy/libs/core/src/types/device-messaging.ts similarity index 100% rename from libs/core/src/types/device-messaging.ts rename to legacy/libs/core/src/types/device-messaging.ts diff --git a/libs/core/src/types/jira-transition.ts b/legacy/libs/core/src/types/jira-transition.ts similarity index 100% rename from libs/core/src/types/jira-transition.ts rename to legacy/libs/core/src/types/jira-transition.ts diff --git a/libs/core/src/types/solcast-params.ts b/legacy/libs/core/src/types/solcast-params.ts similarity index 100% rename from libs/core/src/types/solcast-params.ts rename to legacy/libs/core/src/types/solcast-params.ts diff --git a/libs/core/src/types/solcast-type.ts b/legacy/libs/core/src/types/solcast-type.ts similarity index 100% rename from libs/core/src/types/solcast-type.ts rename to legacy/libs/core/src/types/solcast-type.ts diff --git a/legacy/libs/core/src/types/supabase-types.ts b/legacy/libs/core/src/types/supabase-types.ts new file mode 100644 index 0000000..74d18a9 --- /dev/null +++ b/legacy/libs/core/src/types/supabase-types.ts @@ -0,0 +1,5459 @@ +export type Json = Record + +export type Database = { + // Allows to automatically instantiate createClient with right options + // instead of createClient(URL, KEY) + __InternalSupabase: { + PostgrestVersion: '12.2.3 (519615d)'; + }; + public: { + Tables: { + accounts: { + Row: { + created_at: string; + deleted_at: string | null; + email: string | null; + full_name: string | null; + id: number; + organization_id: number | null; + phone: string | null; + supabase_id: string | null; + telegram_id: string | null; + telegram_link_token: string | null; + }; + Insert: { + created_at?: string; + deleted_at?: string | null; + email?: string | null; + full_name?: string | null; + id?: number; + organization_id?: number | null; + phone?: string | null; + supabase_id?: string | null; + telegram_id?: string | null; + telegram_link_token?: string | null; + }; + Update: { + created_at?: string; + deleted_at?: string | null; + email?: string | null; + full_name?: string | null; + id?: number; + organization_id?: number | null; + phone?: string | null; + supabase_id?: string | null; + telegram_id?: string | null; + telegram_link_token?: string | null; + }; + Relationships: [ + { + foreignKeyName: 'FK_238d61e0f8ac37278f726efac20'; + columns: ['organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + ]; + }; + agents: { + Row: { + account_id: number | null; + created_at: string; + grid_id: number | null; + id: number; + rls_organization_id: number | null; + }; + Insert: { + account_id?: number | null; + created_at?: string; + grid_id?: number | null; + id?: number; + rls_organization_id?: number | null; + }; + Update: { + account_id?: number | null; + created_at?: string; + grid_id?: number | null; + id?: number; + rls_organization_id?: number | null; + }; + Relationships: [ + { + foreignKeyName: 'agents_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_9bb7cb7efc780ec4d3dec34354f'; + columns: ['grid_id']; + isOneToOne: false; + referencedRelation: 'grids'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_df520decc9e003a843d8edd9867'; + columns: ['account_id']; + isOneToOne: true; + referencedRelation: 'accounts'; + referencedColumns: ['id']; + }, + ]; + }; + api_keys: { + Row: { + account_id: number | null; + created_at: string; + id: number; + is_locked: boolean; + key: string | null; + }; + Insert: { + account_id?: number | null; + created_at?: string; + id?: number; + is_locked?: boolean; + key?: string | null; + }; + Update: { + account_id?: number | null; + created_at?: string; + id?: number; + is_locked?: boolean; + key?: string | null; + }; + Relationships: [ + { + foreignKeyName: 'FK_decaf331589778e33441b2a8d9e'; + columns: ['account_id']; + isOneToOne: false; + referencedRelation: 'accounts'; + referencedColumns: ['id']; + }, + ]; + }; + audits: { + Row: { + agent_id: number | null; + author_id: number | null; + connection_id: number | null; + created_at: string; + customer_id: number | null; + dcu_id: number | null; + from_fs_command: boolean; + grid_id: number | null; + id: number; + member_id: number | null; + message: string; + meter_id: number | null; + organization_id: number | null; + }; + Insert: { + agent_id?: number | null; + author_id?: number | null; + connection_id?: number | null; + created_at?: string; + customer_id?: number | null; + dcu_id?: number | null; + from_fs_command?: boolean; + grid_id?: number | null; + id?: number; + member_id?: number | null; + message: string; + meter_id?: number | null; + organization_id?: number | null; + }; + Update: { + agent_id?: number | null; + author_id?: number | null; + connection_id?: number | null; + created_at?: string; + customer_id?: number | null; + dcu_id?: number | null; + from_fs_command?: boolean; + grid_id?: number | null; + id?: number; + member_id?: number | null; + message?: string; + meter_id?: number | null; + organization_id?: number | null; + }; + Relationships: [ + { + foreignKeyName: 'FK_18c521072a6ca9d57365ffb24fe'; + columns: ['agent_id']; + isOneToOne: false; + referencedRelation: 'agents'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_18c521072a6ca9d57365ffb24fe'; + columns: ['agent_id']; + isOneToOne: false; + referencedRelation: 'agents_with_account'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_236a383b1007cf64f0c8c7f6534'; + columns: ['customer_id']; + isOneToOne: false; + referencedRelation: 'customers'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_236a383b1007cf64f0c8c7f6534'; + columns: ['customer_id']; + isOneToOne: false; + referencedRelation: 'customers_with_account'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_2d52a7fc85cfbb0421af977766e'; + columns: ['grid_id']; + isOneToOne: false; + referencedRelation: 'grids'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_2fc5f64b328675b5203d54c7929'; + columns: ['connection_id']; + isOneToOne: false; + referencedRelation: 'connections'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_336b627b254b70b0702436e6aff'; + columns: ['author_id']; + isOneToOne: false; + referencedRelation: 'accounts'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_4a300197465db92edfc5563d20b'; + columns: ['meter_id']; + isOneToOne: false; + referencedRelation: 'meters'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_4a300197465db92edfc5563d20b'; + columns: ['meter_id']; + isOneToOne: false; + referencedRelation: 'meters_with_account_and_statuses'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_abde505f056e2f46c5df2c12491'; + columns: ['organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_e3f3409859562fbe10b78d1399e'; + columns: ['member_id']; + isOneToOne: false; + referencedRelation: 'members'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_eb582acfd9d83af268d328e0b79'; + columns: ['dcu_id']; + isOneToOne: false; + referencedRelation: 'dcus'; + referencedColumns: ['id']; + }, + ]; + }; + autopilot_executions: { + Row: { + code: string | null; + created_at: string; + id: number; + input: Json | null; + output: Json | null; + queries: Json | null; + }; + Insert: { + code?: string | null; + created_at?: string; + id?: number; + input?: Json | null; + output?: Json | null; + queries?: Json | null; + }; + Update: { + code?: string | null; + created_at?: string; + id?: number; + input?: Json | null; + output?: Json | null; + queries?: Json | null; + }; + Relationships: []; + }; + bank_accounts: { + Row: { + bank_id: number | null; + created_at: string; + id: number; + organization_id: number | null; + }; + Insert: { + bank_id?: number | null; + created_at?: string; + id?: number; + organization_id?: number | null; + }; + Update: { + bank_id?: number | null; + created_at?: string; + id?: number; + organization_id?: number | null; + }; + Relationships: [ + { + foreignKeyName: 'FK_482543ba26483726aaa00d39174'; + columns: ['bank_id']; + isOneToOne: false; + referencedRelation: 'banks'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_cc20105b139589c697648c925c3'; + columns: ['organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + ]; + }; + banks: { + Row: { + created_at: string; + external_id: string; + id: number; + name: string; + }; + Insert: { + created_at?: string; + external_id: string; + id?: number; + name: string; + }; + Update: { + created_at?: string; + external_id?: string; + id?: number; + name?: string; + }; + Relationships: []; + }; + connection_requested_meters: { + Row: { + connection_id: number; + created_at: string; + deleted_at: string | null; + fee: number; + id: number; + meter_phase: Database['public']['Enums']['meter_phase_enum']; + meter_type: Database['public']['Enums']['meter_type_enum']; + rls_organization_id: number | null; + }; + Insert: { + connection_id: number; + created_at?: string; + deleted_at?: string | null; + fee?: number; + id?: number; + meter_phase: Database['public']['Enums']['meter_phase_enum']; + meter_type: Database['public']['Enums']['meter_type_enum']; + rls_organization_id?: number | null; + }; + Update: { + connection_id?: number; + created_at?: string; + deleted_at?: string | null; + fee?: number; + id?: number; + meter_phase?: Database['public']['Enums']['meter_phase_enum']; + meter_type?: Database['public']['Enums']['meter_type_enum']; + rls_organization_id?: number | null; + }; + Relationships: [ + { + foreignKeyName: 'connection_requested_meters_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_d390197863e9a88be5e122d9695'; + columns: ['connection_id']; + isOneToOne: false; + referencedRelation: 'connections'; + referencedColumns: ['id']; + }, + ]; + }; + connections: { + Row: { + created_at: string; + currency: Database['public']['Enums']['currency_enum']; + customer_id: number | null; + deleted_at: string | null; + document_id: string | null; + document_type: + | Database['public']['Enums']['id_document_type_enum'] + | null; + external_system: + | Database['public']['Enums']['external_system_enum'] + | null; + id: number; + is_building_wired: boolean | null; + is_commercial: boolean; + is_lifeline: boolean | null; + is_public: boolean; + is_residential: boolean; + is_using_led_bulbs: boolean | null; + paid: number; + rls_organization_id: number | null; + upload_uuid: string | null; + women_impacted: number; + }; + Insert: { + created_at?: string; + currency: Database['public']['Enums']['currency_enum']; + customer_id?: number | null; + deleted_at?: string | null; + document_id?: string | null; + document_type?: + | Database['public']['Enums']['id_document_type_enum'] + | null; + external_system?: + | Database['public']['Enums']['external_system_enum'] + | null; + id?: number; + is_building_wired?: boolean | null; + is_commercial: boolean; + is_lifeline?: boolean | null; + is_public: boolean; + is_residential: boolean; + is_using_led_bulbs?: boolean | null; + paid?: number; + rls_organization_id?: number | null; + upload_uuid?: string | null; + women_impacted: number; + }; + Update: { + created_at?: string; + currency?: Database['public']['Enums']['currency_enum']; + customer_id?: number | null; + deleted_at?: string | null; + document_id?: string | null; + document_type?: + | Database['public']['Enums']['id_document_type_enum'] + | null; + external_system?: + | Database['public']['Enums']['external_system_enum'] + | null; + id?: number; + is_building_wired?: boolean | null; + is_commercial?: boolean; + is_lifeline?: boolean | null; + is_public?: boolean; + is_residential?: boolean; + is_using_led_bulbs?: boolean | null; + paid?: number; + rls_organization_id?: number | null; + upload_uuid?: string | null; + women_impacted?: number; + }; + Relationships: [ + { + foreignKeyName: 'connections_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_37f79eb1e29a53cc582fbb805e0'; + columns: ['customer_id']; + isOneToOne: false; + referencedRelation: 'customers'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_37f79eb1e29a53cc582fbb805e0'; + columns: ['customer_id']; + isOneToOne: false; + referencedRelation: 'customers_with_account'; + referencedColumns: ['id']; + }, + ]; + }; + customers: { + Row: { + account_id: number | null; + created_at: string; + gender: Database['public']['Enums']['gender_enum'] | null; + generator_owned: + | Database['public']['Enums']['generator_type_enum'] + | null; + grid_id: number | null; + id: number; + is_hidden_from_reporting: boolean; + latitude: number | null; + lives_primarily_in_the_community: boolean; + longitude: number | null; + rls_organization_id: number | null; + total_connection_fee: number; + total_connection_paid: number; + }; + Insert: { + account_id?: number | null; + created_at?: string; + gender?: Database['public']['Enums']['gender_enum'] | null; + generator_owned?: + | Database['public']['Enums']['generator_type_enum'] + | null; + grid_id?: number | null; + id?: number; + is_hidden_from_reporting?: boolean; + latitude?: number | null; + lives_primarily_in_the_community?: boolean; + longitude?: number | null; + rls_organization_id?: number | null; + total_connection_fee?: number; + total_connection_paid?: number; + }; + Update: { + account_id?: number | null; + created_at?: string; + gender?: Database['public']['Enums']['gender_enum'] | null; + generator_owned?: + | Database['public']['Enums']['generator_type_enum'] + | null; + grid_id?: number | null; + id?: number; + is_hidden_from_reporting?: boolean; + latitude?: number | null; + lives_primarily_in_the_community?: boolean; + longitude?: number | null; + rls_organization_id?: number | null; + total_connection_fee?: number; + total_connection_paid?: number; + }; + Relationships: [ + { + foreignKeyName: 'customers_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_51a88ee1d4fceb047e7cfda3baa'; + columns: ['grid_id']; + isOneToOne: false; + referencedRelation: 'grids'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_ebcc29963874e55053e8ee80be5'; + columns: ['account_id']; + isOneToOne: true; + referencedRelation: 'accounts'; + referencedColumns: ['id']; + }, + ]; + }; + dcus: { + Row: { + communication_protocol: + | Database['public']['Enums']['communication_protocol_enum'] + | null; + created_at: string; + external_reference: string; + external_system: Database['public']['Enums']['external_system_enum']; + grid_id: number | null; + id: number; + is_online: boolean; + is_online_updated_at: string | null; + last_metering_hardware_install_session_id: number | null; + last_online_at: string | null; + location_geom: unknown; + queue_buffer_length: number; + rls_organization_id: number | null; + }; + Insert: { + communication_protocol?: + | Database['public']['Enums']['communication_protocol_enum'] + | null; + created_at?: string; + external_reference: string; + external_system: Database['public']['Enums']['external_system_enum']; + grid_id?: number | null; + id?: number; + is_online?: boolean; + is_online_updated_at?: string | null; + last_metering_hardware_install_session_id?: number | null; + last_online_at?: string | null; + location_geom?: unknown; + queue_buffer_length?: number; + rls_organization_id?: number | null; + }; + Update: { + communication_protocol?: + | Database['public']['Enums']['communication_protocol_enum'] + | null; + created_at?: string; + external_reference?: string; + external_system?: Database['public']['Enums']['external_system_enum']; + grid_id?: number | null; + id?: number; + is_online?: boolean; + is_online_updated_at?: string | null; + last_metering_hardware_install_session_id?: number | null; + last_online_at?: string | null; + location_geom?: unknown; + queue_buffer_length?: number; + rls_organization_id?: number | null; + }; + Relationships: [ + { + foreignKeyName: 'dcus_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_254c3db39098da62eb3f96f0006'; + columns: ['grid_id']; + isOneToOne: false; + referencedRelation: 'grids'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_9334b587e6e5291f8ccd1b11c44'; + columns: ['last_metering_hardware_install_session_id']; + isOneToOne: true; + referencedRelation: 'metering_hardware_install_sessions'; + referencedColumns: ['id']; + }, + ]; + }; + device_logs: { + Row: { + created_at: string; + device_id: number | null; + id: number; + message: string | null; + rls_organization_id: number | null; + }; + Insert: { + created_at?: string; + device_id?: number | null; + id?: number; + message?: string | null; + rls_organization_id?: number | null; + }; + Update: { + created_at?: string; + device_id?: number | null; + id?: number; + message?: string | null; + rls_organization_id?: number | null; + }; + Relationships: [ + { + foreignKeyName: 'device_logs_device_id_fkey'; + columns: ['device_id']; + isOneToOne: false; + referencedRelation: 'devices'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'device_logs_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + ]; + }; + device_types: { + Row: { + created_at: string; + external_system: + | Database['public']['Enums']['external_system_enum'] + | null; + id: number; + name: string | null; + }; + Insert: { + created_at?: string; + external_system?: + | Database['public']['Enums']['external_system_enum'] + | null; + id?: number; + name?: string | null; + }; + Update: { + created_at?: string; + external_system?: + | Database['public']['Enums']['external_system_enum'] + | null; + id?: number; + name?: string | null; + }; + Relationships: []; + }; + devices: { + Row: { + created_at: string; + device_type_id: number; + external_reference: string; + grid_id: number | null; + id: number; + nickname: string | null; + rls_organization_id: number | null; + values: Json; + }; + Insert: { + created_at?: string; + device_type_id: number; + external_reference: string; + grid_id?: number | null; + id?: number; + nickname?: string | null; + rls_organization_id?: number | null; + values?: Json; + }; + Update: { + created_at?: string; + device_type_id?: number; + external_reference?: string; + grid_id?: number | null; + id?: number; + nickname?: string | null; + rls_organization_id?: number | null; + values?: Json; + }; + Relationships: [ + { + foreignKeyName: 'devices_device_type_id_fkey'; + columns: ['device_type_id']; + isOneToOne: false; + referencedRelation: 'device_types'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'devices_grid_id_fkey'; + columns: ['grid_id']; + isOneToOne: false; + referencedRelation: 'grids'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'devices_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + ]; + }; + directive_batch_executions: { + Row: { + completed_at: string | null; + created_at: string; + directive_batch_id: number; + failed_count: number; + id: number; + pending_count: number; + processed_count: number; + processing_count: number; + qualified_at: string | null; + rls_organization_id: number | null; + successful_count: number; + total_count: number; + }; + Insert: { + completed_at?: string | null; + created_at?: string; + directive_batch_id: number; + failed_count?: number; + id?: number; + pending_count?: number; + processed_count?: number; + processing_count?: number; + qualified_at?: string | null; + rls_organization_id?: number | null; + successful_count?: number; + total_count?: number; + }; + Update: { + completed_at?: string | null; + created_at?: string; + directive_batch_id?: number; + failed_count?: number; + id?: number; + pending_count?: number; + processed_count?: number; + processing_count?: number; + qualified_at?: string | null; + rls_organization_id?: number | null; + successful_count?: number; + total_count?: number; + }; + Relationships: [ + { + foreignKeyName: 'directive_batch_executions_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_7b0737005e7c358392d87ebb329'; + columns: ['directive_batch_id']; + isOneToOne: false; + referencedRelation: 'directive_batches'; + referencedColumns: ['id']; + }, + ]; + }; + directive_batches: { + Row: { + author_id: number | null; + created_at: string; + directive_type: + | Database['public']['Enums']['directive_type_enum'] + | null; + execution_bucket: number | null; + fs_command: + | Database['public']['Enums']['fs_command_type_enum'] + | null; + grid_id: number | null; + hour: number; + id: number; + is_deleted: boolean; + is_repeating: boolean; + lock_session: string | null; + minute: number; + rls_organization_id: number | null; + task_type: + | Database['public']['Enums']['meter_interaction_type_enum'] + | null; + updated_at: string | null; + }; + Insert: { + author_id?: number | null; + created_at?: string; + directive_type?: + | Database['public']['Enums']['directive_type_enum'] + | null; + execution_bucket?: number | null; + fs_command?: + | Database['public']['Enums']['fs_command_type_enum'] + | null; + grid_id?: number | null; + hour: number; + id?: number; + is_deleted?: boolean; + is_repeating?: boolean; + lock_session?: string | null; + minute: number; + rls_organization_id?: number | null; + task_type?: + | Database['public']['Enums']['meter_interaction_type_enum'] + | null; + updated_at?: string | null; + }; + Update: { + author_id?: number | null; + created_at?: string; + directive_type?: + | Database['public']['Enums']['directive_type_enum'] + | null; + execution_bucket?: number | null; + fs_command?: + | Database['public']['Enums']['fs_command_type_enum'] + | null; + grid_id?: number | null; + hour?: number; + id?: number; + is_deleted?: boolean; + is_repeating?: boolean; + lock_session?: string | null; + minute?: number; + rls_organization_id?: number | null; + task_type?: + | Database['public']['Enums']['meter_interaction_type_enum'] + | null; + updated_at?: string | null; + }; + Relationships: [ + { + foreignKeyName: 'directive_batches_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_5c44fae05dc443720f62664e563'; + columns: ['grid_id']; + isOneToOne: false; + referencedRelation: 'grids'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_6e26cb6e966f484f0f897127c84'; + columns: ['author_id']; + isOneToOne: false; + referencedRelation: 'accounts'; + referencedColumns: ['id']; + }, + ]; + }; + directive_watchdog_sessions: { + Row: { + created_at: string; + id: number; + identifier: string | null; + }; + Insert: { + created_at?: string; + id?: number; + identifier?: string | null; + }; + Update: { + created_at?: string; + id?: number; + identifier?: string | null; + }; + Relationships: []; + }; + directives: { + Row: { + author_id: number | null; + can_be_retried: boolean | null; + created_at: string; + directive_batch_deprecated_id: number | null; + directive_batch_execution_id: number | null; + directive_error: + | Database['public']['Enums']['directive_error_enum'] + | null; + directive_priority: number; + directive_special_status: + | Database['public']['Enums']['directive_special_status_enum'] + | null; + directive_status: Database['public']['Enums']['directive_status_enum']; + directive_status_a: + | Database['public']['Enums']['directive_status_enum'] + | null; + directive_status_b: + | Database['public']['Enums']['directive_status_enum'] + | null; + directive_status_c: + | Database['public']['Enums']['directive_status_enum'] + | null; + directive_type: Database['public']['Enums']['directive_type_enum']; + directive_watchdog_session_id: number | null; + execution_session: string | null; + external_reference: string | null; + external_reference_a: string | null; + external_reference_b: string | null; + external_reference_c: string | null; + id: number; + is_on: boolean | null; + kwh: number | null; + kwh_credit_available: number | null; + meter_commissioning_id: number | null; + meter_credit_transfer_id: number | null; + meter_id: number | null; + meter_version: string | null; + order_id: number | null; + power: number | null; + power_a: number | null; + power_b: number | null; + power_c: number | null; + power_down_count: number | null; + power_limit: number | null; + power_limit_should_be: number | null; + retry_of_directive_id: number | null; + rls_organization_id: number | null; + status_check_lock_session: string | null; + status_last_checked_at: string | null; + token: string | null; + updated_at: string | null; + voltage: number | null; + voltage_a: number | null; + voltage_b: number | null; + voltage_c: number | null; + }; + Insert: { + author_id?: number | null; + can_be_retried?: boolean | null; + created_at?: string; + directive_batch_deprecated_id?: number | null; + directive_batch_execution_id?: number | null; + directive_error?: + | Database['public']['Enums']['directive_error_enum'] + | null; + directive_priority?: number; + directive_special_status?: + | Database['public']['Enums']['directive_special_status_enum'] + | null; + directive_status?: Database['public']['Enums']['directive_status_enum']; + directive_status_a?: + | Database['public']['Enums']['directive_status_enum'] + | null; + directive_status_b?: + | Database['public']['Enums']['directive_status_enum'] + | null; + directive_status_c?: + | Database['public']['Enums']['directive_status_enum'] + | null; + directive_type: Database['public']['Enums']['directive_type_enum']; + directive_watchdog_session_id?: number | null; + execution_session?: string | null; + external_reference?: string | null; + external_reference_a?: string | null; + external_reference_b?: string | null; + external_reference_c?: string | null; + id?: number; + is_on?: boolean | null; + kwh?: number | null; + kwh_credit_available?: number | null; + meter_commissioning_id?: number | null; + meter_credit_transfer_id?: number | null; + meter_id?: number | null; + meter_version?: string | null; + order_id?: number | null; + power?: number | null; + power_a?: number | null; + power_b?: number | null; + power_c?: number | null; + power_down_count?: number | null; + power_limit?: number | null; + power_limit_should_be?: number | null; + retry_of_directive_id?: number | null; + rls_organization_id?: number | null; + status_check_lock_session?: string | null; + status_last_checked_at?: string | null; + token?: string | null; + updated_at?: string | null; + voltage?: number | null; + voltage_a?: number | null; + voltage_b?: number | null; + voltage_c?: number | null; + }; + Update: { + author_id?: number | null; + can_be_retried?: boolean | null; + created_at?: string; + directive_batch_deprecated_id?: number | null; + directive_batch_execution_id?: number | null; + directive_error?: + | Database['public']['Enums']['directive_error_enum'] + | null; + directive_priority?: number; + directive_special_status?: + | Database['public']['Enums']['directive_special_status_enum'] + | null; + directive_status?: Database['public']['Enums']['directive_status_enum']; + directive_status_a?: + | Database['public']['Enums']['directive_status_enum'] + | null; + directive_status_b?: + | Database['public']['Enums']['directive_status_enum'] + | null; + directive_status_c?: + | Database['public']['Enums']['directive_status_enum'] + | null; + directive_type?: Database['public']['Enums']['directive_type_enum']; + directive_watchdog_session_id?: number | null; + execution_session?: string | null; + external_reference?: string | null; + external_reference_a?: string | null; + external_reference_b?: string | null; + external_reference_c?: string | null; + id?: number; + is_on?: boolean | null; + kwh?: number | null; + kwh_credit_available?: number | null; + meter_commissioning_id?: number | null; + meter_credit_transfer_id?: number | null; + meter_id?: number | null; + meter_version?: string | null; + order_id?: number | null; + power?: number | null; + power_a?: number | null; + power_b?: number | null; + power_c?: number | null; + power_down_count?: number | null; + power_limit?: number | null; + power_limit_should_be?: number | null; + retry_of_directive_id?: number | null; + rls_organization_id?: number | null; + status_check_lock_session?: string | null; + status_last_checked_at?: string | null; + token?: string | null; + updated_at?: string | null; + voltage?: number | null; + voltage_a?: number | null; + voltage_b?: number | null; + voltage_c?: number | null; + }; + Relationships: [ + { + foreignKeyName: 'directives_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_259bc4fc1c63035217ee41a719d'; + columns: ['meter_commissioning_id']; + isOneToOne: false; + referencedRelation: 'meter_commissionings'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_276c89b475e9805d3322a36657a'; + columns: ['order_id']; + isOneToOne: false; + referencedRelation: 'orders'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_3f3cc652d521872dbfe604b05b6'; + columns: ['meter_credit_transfer_id']; + isOneToOne: false; + referencedRelation: 'meter_credit_transfers'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_637c697dc2877f08538b2fa02f5'; + columns: ['directive_watchdog_session_id']; + isOneToOne: false; + referencedRelation: 'directive_watchdog_sessions'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_6c4e7feadae61cb27761734b11d'; + columns: ['meter_id']; + isOneToOne: false; + referencedRelation: 'meters'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_6c4e7feadae61cb27761734b11d'; + columns: ['meter_id']; + isOneToOne: false; + referencedRelation: 'meters_with_account_and_statuses'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_9064328678b805e99750773387d'; + columns: ['retry_of_directive_id']; + isOneToOne: false; + referencedRelation: 'directives'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_f3a84a3f4b619b1ca8c179fff6c'; + columns: ['author_id']; + isOneToOne: false; + referencedRelation: 'accounts'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_f9e3439b6ea7096d6a9d85c4503'; + columns: ['directive_batch_execution_id']; + isOneToOne: false; + referencedRelation: 'directive_batch_executions'; + referencedColumns: ['id']; + }, + ]; + }; + energy_cabins: { + Row: { + created_at: string; + grid_id: number; + id: number; + location_geom: unknown; + rls_organization_id: number | null; + }; + Insert: { + created_at?: string; + grid_id: number; + id?: number; + location_geom: unknown; + rls_organization_id?: number | null; + }; + Update: { + created_at?: string; + grid_id?: number; + id?: number; + location_geom?: unknown; + rls_organization_id?: number | null; + }; + Relationships: [ + { + foreignKeyName: 'energy_cabins_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_ba81ffc1506025a549849f06f36'; + columns: ['grid_id']; + isOneToOne: false; + referencedRelation: 'grids'; + referencedColumns: ['id']; + }, + ]; + }; + features: { + Row: { + created_at: string; + id: number; + is_deleted: boolean; + uuid: string; + }; + Insert: { + created_at?: string; + id?: number; + is_deleted?: boolean; + uuid: string; + }; + Update: { + created_at?: string; + id?: number; + is_deleted?: boolean; + uuid?: string; + }; + Relationships: []; + }; + grids: { + Row: { + are_all_dcus_online: boolean; + are_all_dcus_under_high_load_threshold: boolean; + commissioned_at: string | null; + created_at: string; + current_weather: + | Database['public']['Enums']['weather_type_enum'] + | null; + default_fs_1_phase_connection_fee: number; + default_fs_3_phase_connection_fee: number; + default_hps_connection_fee: number; + deleted_at: string | null; + deployed_at: string | null; + feature_access_config: Json; + generation_external_gateway_id: string | null; + generation_external_site_id: string | null; + generation_external_system: Database['public']['Enums']['external_system_enum']; + generation_gateway_last_seen_at: string | null; + id: number; + identifier: number | null; + internal_telegram_group_chat_id: string | null; + internal_telegram_group_thread_id: string | null; + is_automatic_energy_generation_data_sync_enabled: boolean; + is_automatic_meter_energy_consumption_data_sync_enabled: boolean; + is_automatic_meter_install_enabled: boolean; + is_automatic_payout_generation_enabled: boolean; + is_cabin_meter_credit_depleting: boolean; + is_dcu_connectivity_tracking_enabled: boolean; + is_energised_notification_enabled: boolean; + is_fs_control_rule_change_notification_enabled: boolean; + is_fs_on: boolean; + is_fs_on_notification_enabled: boolean; + is_fs_on_updated_at: string | null; + is_generation_managed_by_nxt_grid: boolean; + is_hidden_from_reporting: boolean; + is_hps_on: boolean; + is_hps_on_threshold_kw: number; + is_hps_on_updated_at: string | null; + is_metering_hardware_online_notification_enabled: boolean; + is_panel_cleaning_notification_enabled: boolean; + is_router_connectivity_tracking_enabled: boolean; + is_tariff_change_notification_enabled: boolean; + is_three_phase_supported: boolean; + is_upcoming_fs_control_rule_notification_enabled: boolean; + is_using_mobile_network: boolean; + is_using_vsat: boolean; + kwh: number; + kwh_per_battery_module: number | null; + kwh_tariff: number; + kwh_tariff_essential_service: number; + kwh_tariff_full_service: number; + kwp: number; + kwp_tariff: number; + lifeline_connection_days_threshold: number; + lifeline_connection_kwh_threshold: number; + location_geom: unknown; + meter_commissioning_initial_credit_kwh: number; + meter_communication_issue_threshold_detection_days: number; + meter_consumption_issue_threshold_detection_days: number; + metering_external_system: Database['public']['Enums']['external_system_enum']; + monthly_rental: number; + name: string; + organization_id: number; + should_fs_be_on: boolean; + should_fs_be_on_updated_at: string | null; + telegram_config: Json | null; + telegram_notification_channel_invite_link: string | null; + telegram_response_path_autopilot: string | null; + telegram_response_path_token: string | null; + timezone: string; + uses_dual_meter_setup: boolean; + walkthrough_external_id: string | null; + }; + Insert: { + are_all_dcus_online?: boolean; + are_all_dcus_under_high_load_threshold?: boolean; + commissioned_at?: string | null; + created_at?: string; + current_weather?: + | Database['public']['Enums']['weather_type_enum'] + | null; + default_fs_1_phase_connection_fee?: number; + default_fs_3_phase_connection_fee?: number; + default_hps_connection_fee?: number; + deleted_at?: string | null; + deployed_at?: string | null; + feature_access_config?: Json; + generation_external_gateway_id?: string | null; + generation_external_site_id?: string | null; + generation_external_system?: Database['public']['Enums']['external_system_enum']; + generation_gateway_last_seen_at?: string | null; + id?: number; + identifier?: number | null; + internal_telegram_group_chat_id?: string | null; + internal_telegram_group_thread_id?: string | null; + is_automatic_energy_generation_data_sync_enabled?: boolean; + is_automatic_meter_energy_consumption_data_sync_enabled?: boolean; + is_automatic_meter_install_enabled?: boolean; + is_automatic_payout_generation_enabled?: boolean; + is_cabin_meter_credit_depleting?: boolean; + is_dcu_connectivity_tracking_enabled?: boolean; + is_energised_notification_enabled?: boolean; + is_fs_control_rule_change_notification_enabled?: boolean; + is_fs_on?: boolean; + is_fs_on_notification_enabled?: boolean; + is_fs_on_updated_at?: string | null; + is_generation_managed_by_nxt_grid?: boolean; + is_hidden_from_reporting?: boolean; + is_hps_on?: boolean; + is_hps_on_threshold_kw?: number; + is_hps_on_updated_at?: string | null; + is_metering_hardware_online_notification_enabled?: boolean; + is_panel_cleaning_notification_enabled?: boolean; + is_router_connectivity_tracking_enabled?: boolean; + is_tariff_change_notification_enabled?: boolean; + is_three_phase_supported?: boolean; + is_upcoming_fs_control_rule_notification_enabled?: boolean; + is_using_mobile_network?: boolean; + is_using_vsat?: boolean; + kwh?: number; + kwh_per_battery_module?: number | null; + kwh_tariff?: number; + kwh_tariff_essential_service?: number; + kwh_tariff_full_service?: number; + kwp?: number; + kwp_tariff?: number; + lifeline_connection_days_threshold?: number; + lifeline_connection_kwh_threshold?: number; + location_geom?: unknown; + meter_commissioning_initial_credit_kwh?: number; + meter_communication_issue_threshold_detection_days?: number; + meter_consumption_issue_threshold_detection_days?: number; + metering_external_system?: Database['public']['Enums']['external_system_enum']; + monthly_rental?: number; + name: string; + organization_id: number; + should_fs_be_on?: boolean; + should_fs_be_on_updated_at?: string | null; + telegram_config?: Json | null; + telegram_notification_channel_invite_link?: string | null; + telegram_response_path_autopilot?: string | null; + telegram_response_path_token?: string | null; + timezone?: string; + uses_dual_meter_setup?: boolean; + walkthrough_external_id?: string | null; + }; + Update: { + are_all_dcus_online?: boolean; + are_all_dcus_under_high_load_threshold?: boolean; + commissioned_at?: string | null; + created_at?: string; + current_weather?: + | Database['public']['Enums']['weather_type_enum'] + | null; + default_fs_1_phase_connection_fee?: number; + default_fs_3_phase_connection_fee?: number; + default_hps_connection_fee?: number; + deleted_at?: string | null; + deployed_at?: string | null; + feature_access_config?: Json; + generation_external_gateway_id?: string | null; + generation_external_site_id?: string | null; + generation_external_system?: Database['public']['Enums']['external_system_enum']; + generation_gateway_last_seen_at?: string | null; + id?: number; + identifier?: number | null; + internal_telegram_group_chat_id?: string | null; + internal_telegram_group_thread_id?: string | null; + is_automatic_energy_generation_data_sync_enabled?: boolean; + is_automatic_meter_energy_consumption_data_sync_enabled?: boolean; + is_automatic_meter_install_enabled?: boolean; + is_automatic_payout_generation_enabled?: boolean; + is_cabin_meter_credit_depleting?: boolean; + is_dcu_connectivity_tracking_enabled?: boolean; + is_energised_notification_enabled?: boolean; + is_fs_control_rule_change_notification_enabled?: boolean; + is_fs_on?: boolean; + is_fs_on_notification_enabled?: boolean; + is_fs_on_updated_at?: string | null; + is_generation_managed_by_nxt_grid?: boolean; + is_hidden_from_reporting?: boolean; + is_hps_on?: boolean; + is_hps_on_threshold_kw?: number; + is_hps_on_updated_at?: string | null; + is_metering_hardware_online_notification_enabled?: boolean; + is_panel_cleaning_notification_enabled?: boolean; + is_router_connectivity_tracking_enabled?: boolean; + is_tariff_change_notification_enabled?: boolean; + is_three_phase_supported?: boolean; + is_upcoming_fs_control_rule_notification_enabled?: boolean; + is_using_mobile_network?: boolean; + is_using_vsat?: boolean; + kwh?: number; + kwh_per_battery_module?: number | null; + kwh_tariff?: number; + kwh_tariff_essential_service?: number; + kwh_tariff_full_service?: number; + kwp?: number; + kwp_tariff?: number; + lifeline_connection_days_threshold?: number; + lifeline_connection_kwh_threshold?: number; + location_geom?: unknown; + meter_commissioning_initial_credit_kwh?: number; + meter_communication_issue_threshold_detection_days?: number; + meter_consumption_issue_threshold_detection_days?: number; + metering_external_system?: Database['public']['Enums']['external_system_enum']; + monthly_rental?: number; + name?: string; + organization_id?: number; + should_fs_be_on?: boolean; + should_fs_be_on_updated_at?: string | null; + telegram_config?: Json | null; + telegram_notification_channel_invite_link?: string | null; + telegram_response_path_autopilot?: string | null; + telegram_response_path_token?: string | null; + timezone?: string; + uses_dual_meter_setup?: boolean; + walkthrough_external_id?: string | null; + }; + Relationships: [ + { + foreignKeyName: 'FK_433ef6a589cd535dff42b34612e'; + columns: ['organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + ]; + }; + issues: { + Row: { + closed_at: string | null; + created_at: string; + estimated_lost_revenue: number; + external_reference: string | null; + external_system: Database['public']['Enums']['external_system_enum']; + grid_id: number | null; + id: number; + issue_status: Database['public']['Enums']['issue_status_enum']; + issue_type: Database['public']['Enums']['issue_type_enum']; + meter_id: number | null; + mppt_id: number | null; + rls_organization_id: number | null; + snoozed_until: string | null; + started_at: string | null; + }; + Insert: { + closed_at?: string | null; + created_at?: string; + estimated_lost_revenue?: number; + external_reference?: string | null; + external_system?: Database['public']['Enums']['external_system_enum']; + grid_id?: number | null; + id?: number; + issue_status?: Database['public']['Enums']['issue_status_enum']; + issue_type: Database['public']['Enums']['issue_type_enum']; + meter_id?: number | null; + mppt_id?: number | null; + rls_organization_id?: number | null; + snoozed_until?: string | null; + started_at?: string | null; + }; + Update: { + closed_at?: string | null; + created_at?: string; + estimated_lost_revenue?: number; + external_reference?: string | null; + external_system?: Database['public']['Enums']['external_system_enum']; + grid_id?: number | null; + id?: number; + issue_status?: Database['public']['Enums']['issue_status_enum']; + issue_type?: Database['public']['Enums']['issue_type_enum']; + meter_id?: number | null; + mppt_id?: number | null; + rls_organization_id?: number | null; + snoozed_until?: string | null; + started_at?: string | null; + }; + Relationships: [ + { + foreignKeyName: 'FK_7b15f3fbbedf51a1a3d21583b7e'; + columns: ['meter_id']; + isOneToOne: false; + referencedRelation: 'meters'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_7b15f3fbbedf51a1a3d21583b7e'; + columns: ['meter_id']; + isOneToOne: false; + referencedRelation: 'meters_with_account_and_statuses'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_be8d64bd0c97a739ac323dde9dd'; + columns: ['grid_id']; + isOneToOne: false; + referencedRelation: 'grids'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_bf46db804a794d1d09eb050ac77'; + columns: ['mppt_id']; + isOneToOne: false; + referencedRelation: 'mppts'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'issues_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + ]; + }; + lorawan_directives: { + Row: { + author_id: number | null; + can_be_retried: boolean | null; + created_at: string; + current_a: number | null; + current_b: number | null; + current_c: number | null; + directive_batch_execution_id: number | null; + directive_direction: Database['public']['Enums']['directive_direction_enum']; + directive_error: + | Database['public']['Enums']['directive_error_enum'] + | null; + directive_phase: + | Database['public']['Enums']['directive_phase_enum'] + | null; + directive_status: Database['public']['Enums']['directive_status_enum']; + directive_type: + | Database['public']['Enums']['directive_type_enum'] + | null; + directive_watchdog_session_id: number | null; + external_reference: string | null; + id: number; + is_on: boolean | null; + kwh: number | null; + kwh_credit_available: number | null; + meter_commissioning_id: number | null; + meter_id: number | null; + order_id: number | null; + power_a: number | null; + power_b: number | null; + power_c: number | null; + power_limit: number | null; + power_limit_should_be: number | null; + retry_of_directive_id: number | null; + rls_organization_id: number | null; + token: string | null; + value: Json | null; + voltage_a: number | null; + voltage_b: number | null; + voltage_c: number | null; + }; + Insert: { + author_id?: number | null; + can_be_retried?: boolean | null; + created_at?: string; + current_a?: number | null; + current_b?: number | null; + current_c?: number | null; + directive_batch_execution_id?: number | null; + directive_direction: Database['public']['Enums']['directive_direction_enum']; + directive_error?: + | Database['public']['Enums']['directive_error_enum'] + | null; + directive_phase?: + | Database['public']['Enums']['directive_phase_enum'] + | null; + directive_status: Database['public']['Enums']['directive_status_enum']; + directive_type?: + | Database['public']['Enums']['directive_type_enum'] + | null; + directive_watchdog_session_id?: number | null; + external_reference?: string | null; + id?: number; + is_on?: boolean | null; + kwh?: number | null; + kwh_credit_available?: number | null; + meter_commissioning_id?: number | null; + meter_id?: number | null; + order_id?: number | null; + power_a?: number | null; + power_b?: number | null; + power_c?: number | null; + power_limit?: number | null; + power_limit_should_be?: number | null; + retry_of_directive_id?: number | null; + rls_organization_id?: number | null; + token?: string | null; + value?: Json | null; + voltage_a?: number | null; + voltage_b?: number | null; + voltage_c?: number | null; + }; + Update: { + author_id?: number | null; + can_be_retried?: boolean | null; + created_at?: string; + current_a?: number | null; + current_b?: number | null; + current_c?: number | null; + directive_batch_execution_id?: number | null; + directive_direction?: Database['public']['Enums']['directive_direction_enum']; + directive_error?: + | Database['public']['Enums']['directive_error_enum'] + | null; + directive_phase?: + | Database['public']['Enums']['directive_phase_enum'] + | null; + directive_status?: Database['public']['Enums']['directive_status_enum']; + directive_type?: + | Database['public']['Enums']['directive_type_enum'] + | null; + directive_watchdog_session_id?: number | null; + external_reference?: string | null; + id?: number; + is_on?: boolean | null; + kwh?: number | null; + kwh_credit_available?: number | null; + meter_commissioning_id?: number | null; + meter_id?: number | null; + order_id?: number | null; + power_a?: number | null; + power_b?: number | null; + power_c?: number | null; + power_limit?: number | null; + power_limit_should_be?: number | null; + retry_of_directive_id?: number | null; + rls_organization_id?: number | null; + token?: string | null; + value?: Json | null; + voltage_a?: number | null; + voltage_b?: number | null; + voltage_c?: number | null; + }; + Relationships: [ + { + foreignKeyName: 'lorawan_directives_author_id_fkey'; + columns: ['author_id']; + isOneToOne: false; + referencedRelation: 'accounts'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'lorawan_directives_directive_batch_execution_id_fkey'; + columns: ['directive_batch_execution_id']; + isOneToOne: false; + referencedRelation: 'directive_batch_executions'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'lorawan_directives_directive_watchdog_session_id_fkey'; + columns: ['directive_watchdog_session_id']; + isOneToOne: false; + referencedRelation: 'directive_watchdog_sessions'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'lorawan_directives_meter_commissioning_id_fkey'; + columns: ['meter_commissioning_id']; + isOneToOne: false; + referencedRelation: 'meter_commissionings'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'lorawan_directives_meter_id_fkey'; + columns: ['meter_id']; + isOneToOne: false; + referencedRelation: 'meters'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'lorawan_directives_meter_id_fkey'; + columns: ['meter_id']; + isOneToOne: false; + referencedRelation: 'meters_with_account_and_statuses'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'lorawan_directives_order_id_fkey'; + columns: ['order_id']; + isOneToOne: false; + referencedRelation: 'orders'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'lorawan_directives_retry_of_directive_id_fkey'; + columns: ['retry_of_directive_id']; + isOneToOne: false; + referencedRelation: 'lorawan_directives'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'lorawan_directives_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + ]; + }; + member_feature: { + Row: { + feature_id: number; + member_id: number; + }; + Insert: { + feature_id: number; + member_id: number; + }; + Update: { + feature_id?: number; + member_id?: number; + }; + Relationships: [ + { + foreignKeyName: 'FK_4da956d70d1de38087b251b9978'; + columns: ['feature_id']; + isOneToOne: false; + referencedRelation: 'features'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_c312f698867204bf4f1fd149c91'; + columns: ['member_id']; + isOneToOne: false; + referencedRelation: 'members'; + referencedColumns: ['id']; + }, + ]; + }; + members: { + Row: { + account_id: number | null; + busy_commissioning_id: number | null; + created_at: string; + hidden: boolean; + id: number; + member_type: Database['public']['Enums']['member_type_enum']; + rls_organization_id: number | null; + subscribed_to_telegram_revenue_notifications: boolean; + training_level: number; + }; + Insert: { + account_id?: number | null; + busy_commissioning_id?: number | null; + created_at?: string; + hidden?: boolean; + id?: number; + member_type?: Database['public']['Enums']['member_type_enum']; + rls_organization_id?: number | null; + subscribed_to_telegram_revenue_notifications?: boolean; + training_level?: number; + }; + Update: { + account_id?: number | null; + busy_commissioning_id?: number | null; + created_at?: string; + hidden?: boolean; + id?: number; + member_type?: Database['public']['Enums']['member_type_enum']; + rls_organization_id?: number | null; + subscribed_to_telegram_revenue_notifications?: boolean; + training_level?: number; + }; + Relationships: [ + { + foreignKeyName: 'FK_a9da2a35fdd2cd46a7bfc57fd73'; + columns: ['busy_commissioning_id']; + isOneToOne: false; + referencedRelation: 'grids'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_fd9dfb97e21b75fc45d42aa614a'; + columns: ['account_id']; + isOneToOne: true; + referencedRelation: 'accounts'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'members_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + ]; + }; + meter_commissionings: { + Row: { + created_at: string; + failed_steps: number | null; + id: number; + initialised_steps: number | null; + lock_session: string | null; + meter_commissioning_status: Database['public']['Enums']['meter_commissioning_status_enum']; + metering_hardware_install_session_id: number | null; + pending_steps: number | null; + processing_steps: number | null; + rls_organization_id: number | null; + successful_steps: number | null; + total_steps: number | null; + }; + Insert: { + created_at?: string; + failed_steps?: number | null; + id?: number; + initialised_steps?: number | null; + lock_session?: string | null; + meter_commissioning_status?: Database['public']['Enums']['meter_commissioning_status_enum']; + metering_hardware_install_session_id?: number | null; + pending_steps?: number | null; + processing_steps?: number | null; + rls_organization_id?: number | null; + successful_steps?: number | null; + total_steps?: number | null; + }; + Update: { + created_at?: string; + failed_steps?: number | null; + id?: number; + initialised_steps?: number | null; + lock_session?: string | null; + meter_commissioning_status?: Database['public']['Enums']['meter_commissioning_status_enum']; + metering_hardware_install_session_id?: number | null; + pending_steps?: number | null; + processing_steps?: number | null; + rls_organization_id?: number | null; + successful_steps?: number | null; + total_steps?: number | null; + }; + Relationships: [ + { + foreignKeyName: 'FK_6684f0790c54ed3d441f78df91a'; + columns: ['metering_hardware_install_session_id']; + isOneToOne: false; + referencedRelation: 'metering_hardware_install_sessions'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'meter_commissionings_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + ]; + }; + meter_credit_transfers: { + Row: { + amount: number; + author_id: number | null; + created_at: string; + currency: Database['public']['Enums']['currency_enum']; + id: number; + lock_session: string | null; + meter_credit_transfer_status: Database['public']['Enums']['meter_credit_transfer_status_enum']; + receiver_meter_id: number; + rls_organization_id: number | null; + sender_meter_id: number; + sender_meter_set_to_amount: number | null; + }; + Insert: { + amount: number; + author_id?: number | null; + created_at?: string; + currency: Database['public']['Enums']['currency_enum']; + id?: number; + lock_session?: string | null; + meter_credit_transfer_status?: Database['public']['Enums']['meter_credit_transfer_status_enum']; + receiver_meter_id: number; + rls_organization_id?: number | null; + sender_meter_id: number; + sender_meter_set_to_amount?: number | null; + }; + Update: { + amount?: number; + author_id?: number | null; + created_at?: string; + currency?: Database['public']['Enums']['currency_enum']; + id?: number; + lock_session?: string | null; + meter_credit_transfer_status?: Database['public']['Enums']['meter_credit_transfer_status_enum']; + receiver_meter_id?: number; + rls_organization_id?: number | null; + sender_meter_id?: number; + sender_meter_set_to_amount?: number | null; + }; + Relationships: [ + { + foreignKeyName: 'FK_3b4ebdcd826c46e4b5c165a0840'; + columns: ['sender_meter_id']; + isOneToOne: false; + referencedRelation: 'meters'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_3b4ebdcd826c46e4b5c165a0840'; + columns: ['sender_meter_id']; + isOneToOne: false; + referencedRelation: 'meters_with_account_and_statuses'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_b196e2b2b291716e18a11611350'; + columns: ['author_id']; + isOneToOne: false; + referencedRelation: 'accounts'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_e83e8905a777a669de1c2391b77'; + columns: ['receiver_meter_id']; + isOneToOne: false; + referencedRelation: 'meters'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_e83e8905a777a669de1c2391b77'; + columns: ['receiver_meter_id']; + isOneToOne: false; + referencedRelation: 'meters_with_account_and_statuses'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'meter_credit_transfers_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + ]; + }; + meter_interactions: { + Row: { + batch_execution_id: number | null; + created_at: string; + delivery_failure_history: Json | null; + id: number; + meter_commissioning_id: number | null; + meter_id: number; + meter_interaction_status: Database['public']['Enums']['meter_interaction_status_enum']; + meter_interaction_type: Database['public']['Enums']['meter_interaction_type_enum']; + order_id: number | null; + payload_data: Json | null; + result_value: Json | null; + target_power_limit: number | null; + token: string | null; + transactive_kwh: number | null; + updated_at: string; + }; + Insert: { + batch_execution_id?: number | null; + created_at?: string; + delivery_failure_history?: Json | null; + id?: number; + meter_commissioning_id?: number | null; + meter_id: number; + meter_interaction_status?: Database['public']['Enums']['meter_interaction_status_enum']; + meter_interaction_type: Database['public']['Enums']['meter_interaction_type_enum']; + order_id?: number | null; + payload_data?: Json | null; + result_value?: Json | null; + target_power_limit?: number | null; + token?: string | null; + transactive_kwh?: number | null; + updated_at?: string; + }; + Update: { + batch_execution_id?: number | null; + created_at?: string; + delivery_failure_history?: Json | null; + id?: number; + meter_commissioning_id?: number | null; + meter_id?: number; + meter_interaction_status?: Database['public']['Enums']['meter_interaction_status_enum']; + meter_interaction_type?: Database['public']['Enums']['meter_interaction_type_enum']; + order_id?: number | null; + payload_data?: Json | null; + result_value?: Json | null; + target_power_limit?: number | null; + token?: string | null; + transactive_kwh?: number | null; + updated_at?: string; + }; + Relationships: [ + { + foreignKeyName: 'meter_interactions_batch_execution_id_fkey'; + columns: ['batch_execution_id']; + isOneToOne: false; + referencedRelation: 'directive_batch_executions'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'meter_interactions_meter_commissioning_id_fkey'; + columns: ['meter_commissioning_id']; + isOneToOne: false; + referencedRelation: 'meter_commissionings'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'meter_interactions_meter_id_fkey'; + columns: ['meter_id']; + isOneToOne: false; + referencedRelation: 'meters'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'meter_interactions_meter_id_fkey'; + columns: ['meter_id']; + isOneToOne: false; + referencedRelation: 'meters_with_account_and_statuses'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'meter_interactions_order_id_fkey'; + columns: ['order_id']; + isOneToOne: true; + referencedRelation: 'orders'; + referencedColumns: ['id']; + }, + ]; + }; + metering_hardware_imports: { + Row: { + created_at: string; + id: number; + lock_session: string | null; + metering_hardware_import_operation: Database['public']['Enums']['mhi_operation_enum']; + metering_hardware_import_status: Database['public']['Enums']['mhi_status_enum']; + metering_hardware_install_session_id: number; + rls_organization_id: number | null; + }; + Insert: { + created_at?: string; + id?: number; + lock_session?: string | null; + metering_hardware_import_operation?: Database['public']['Enums']['mhi_operation_enum']; + metering_hardware_import_status?: Database['public']['Enums']['mhi_status_enum']; + metering_hardware_install_session_id: number; + rls_organization_id?: number | null; + }; + Update: { + created_at?: string; + id?: number; + lock_session?: string | null; + metering_hardware_import_operation?: Database['public']['Enums']['mhi_operation_enum']; + metering_hardware_import_status?: Database['public']['Enums']['mhi_status_enum']; + metering_hardware_install_session_id?: number; + rls_organization_id?: number | null; + }; + Relationships: [ + { + foreignKeyName: 'FK_5c2bb31b2e45c3f1da0b72071a2'; + columns: ['metering_hardware_install_session_id']; + isOneToOne: false; + referencedRelation: 'metering_hardware_install_sessions'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'metering_hardware_imports_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + ]; + }; + metering_hardware_install_sessions: { + Row: { + author_id: number | null; + created_at: string; + dcu_id: number | null; + id: number; + last_meter_commissioning_id: number | null; + last_metering_hardware_import_id: number | null; + meter_id: number | null; + rls_organization_id: number | null; + }; + Insert: { + author_id?: number | null; + created_at?: string; + dcu_id?: number | null; + id?: number; + last_meter_commissioning_id?: number | null; + last_metering_hardware_import_id?: number | null; + meter_id?: number | null; + rls_organization_id?: number | null; + }; + Update: { + author_id?: number | null; + created_at?: string; + dcu_id?: number | null; + id?: number; + last_meter_commissioning_id?: number | null; + last_metering_hardware_import_id?: number | null; + meter_id?: number | null; + rls_organization_id?: number | null; + }; + Relationships: [ + { + foreignKeyName: 'FK_35239534a89f0d6dab2612645eb'; + columns: ['author_id']; + isOneToOne: false; + referencedRelation: 'accounts'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_3f770c153b3333ad1d29cfbf784'; + columns: ['meter_id']; + isOneToOne: false; + referencedRelation: 'meters'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_3f770c153b3333ad1d29cfbf784'; + columns: ['meter_id']; + isOneToOne: false; + referencedRelation: 'meters_with_account_and_statuses'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_4eb64f5decb7250cfa993410c1e'; + columns: ['last_meter_commissioning_id']; + isOneToOne: true; + referencedRelation: 'meter_commissionings'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_be64a09dc738420a409cb960264'; + columns: ['last_metering_hardware_import_id']; + isOneToOne: true; + referencedRelation: 'metering_hardware_imports'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_f3c53f4b922a011aa53d9e0b1ad'; + columns: ['dcu_id']; + isOneToOne: false; + referencedRelation: 'dcus'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'metering_hardware_install_sessions_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + ]; + }; + meters: { + Row: { + balance: number | null; + balance_updated_at: string | null; + communication_protocol: + | Database['public']['Enums']['communication_protocol_enum'] + | null; + connection_id: number | null; + connection_metrics: Json | null; + coord_accuracy: number; + created_at: string; + current_special_status: + | Database['public']['Enums']['directive_special_status_enum'] + | null; + dcu_id: number | null; + decoder_key: string | null; + deleted_at: string | null; + device_id: number | null; + external_reference: string; + external_system: Database['public']['Enums']['external_system_enum']; + id: number; + is_cabin_meter: boolean; + is_manual_mode_on: boolean; + is_manual_mode_on_updated_at: string | null; + is_on: boolean | null; + is_on_updated_at: string | null; + is_simulated: boolean; + is_starred: boolean; + is_test_mode_on: boolean; + issue_check_execution_session: string | null; + issue_check_last_run_at: string | null; + kwh_credit_available: number | null; + kwh_credit_available_updated_at: string | null; + kwh_tariff: number | null; + last_encountered_issue_id: number | null; + last_metering_hardware_install_session_id: number | null; + last_non_zero_consumption_at: string | null; + last_seen_at: string | null; + last_sts_token_issued_at: string | null; + latitude: number | null; + longitude: number | null; + meter_phase: Database['public']['Enums']['meter_phase_enum']; + meter_type: Database['public']['Enums']['meter_type_enum']; + nickname: string | null; + pole_id: number | null; + power: number | null; + power_down_count: number | null; + power_down_count_updated_at: string | null; + power_limit: number | null; + power_limit_hps_mode: number; + power_limit_should_be: number | null; + power_limit_should_be_updated_at: string | null; + power_limit_updated_at: string | null; + power_updated_at: string | null; + pulse_counter_kwh: number | null; + pulse_counter_kwh_updated_at: string | null; + rls_grid_id: number | null; + rls_organization_id: number | null; + should_be_on: boolean | null; + should_be_on_updated_at: string | null; + version: string | null; + voltage: number | null; + voltage_updated_at: string | null; + watchdog_last_run_at: string | null; + watchdog_session: string | null; + }; + Insert: { + balance?: number | null; + balance_updated_at?: string | null; + communication_protocol?: + | Database['public']['Enums']['communication_protocol_enum'] + | null; + connection_id?: number | null; + connection_metrics?: Json | null; + coord_accuracy?: number; + created_at?: string; + current_special_status?: + | Database['public']['Enums']['directive_special_status_enum'] + | null; + dcu_id?: number | null; + decoder_key?: string | null; + deleted_at?: string | null; + device_id?: number | null; + external_reference: string; + external_system: Database['public']['Enums']['external_system_enum']; + id?: number; + is_cabin_meter?: boolean; + is_manual_mode_on?: boolean; + is_manual_mode_on_updated_at?: string | null; + is_on?: boolean | null; + is_on_updated_at?: string | null; + is_simulated?: boolean; + is_starred?: boolean; + is_test_mode_on?: boolean; + issue_check_execution_session?: string | null; + issue_check_last_run_at?: string | null; + kwh_credit_available?: number | null; + kwh_credit_available_updated_at?: string | null; + kwh_tariff?: number | null; + last_encountered_issue_id?: number | null; + last_metering_hardware_install_session_id?: number | null; + last_non_zero_consumption_at?: string | null; + last_seen_at?: string | null; + last_sts_token_issued_at?: string | null; + latitude?: number | null; + longitude?: number | null; + meter_phase?: Database['public']['Enums']['meter_phase_enum']; + meter_type?: Database['public']['Enums']['meter_type_enum']; + nickname?: string | null; + pole_id?: number | null; + power?: number | null; + power_down_count?: number | null; + power_down_count_updated_at?: string | null; + power_limit?: number | null; + power_limit_hps_mode?: number; + power_limit_should_be?: number | null; + power_limit_should_be_updated_at?: string | null; + power_limit_updated_at?: string | null; + power_updated_at?: string | null; + pulse_counter_kwh?: number | null; + pulse_counter_kwh_updated_at?: string | null; + rls_grid_id?: number | null; + rls_organization_id?: number | null; + should_be_on?: boolean | null; + should_be_on_updated_at?: string | null; + version?: string | null; + voltage?: number | null; + voltage_updated_at?: string | null; + watchdog_last_run_at?: string | null; + watchdog_session?: string | null; + }; + Update: { + balance?: number | null; + balance_updated_at?: string | null; + communication_protocol?: + | Database['public']['Enums']['communication_protocol_enum'] + | null; + connection_id?: number | null; + connection_metrics?: Json | null; + coord_accuracy?: number; + created_at?: string; + current_special_status?: + | Database['public']['Enums']['directive_special_status_enum'] + | null; + dcu_id?: number | null; + decoder_key?: string | null; + deleted_at?: string | null; + device_id?: number | null; + external_reference?: string; + external_system?: Database['public']['Enums']['external_system_enum']; + id?: number; + is_cabin_meter?: boolean; + is_manual_mode_on?: boolean; + is_manual_mode_on_updated_at?: string | null; + is_on?: boolean | null; + is_on_updated_at?: string | null; + is_simulated?: boolean; + is_starred?: boolean; + is_test_mode_on?: boolean; + issue_check_execution_session?: string | null; + issue_check_last_run_at?: string | null; + kwh_credit_available?: number | null; + kwh_credit_available_updated_at?: string | null; + kwh_tariff?: number | null; + last_encountered_issue_id?: number | null; + last_metering_hardware_install_session_id?: number | null; + last_non_zero_consumption_at?: string | null; + last_seen_at?: string | null; + last_sts_token_issued_at?: string | null; + latitude?: number | null; + longitude?: number | null; + meter_phase?: Database['public']['Enums']['meter_phase_enum']; + meter_type?: Database['public']['Enums']['meter_type_enum']; + nickname?: string | null; + pole_id?: number | null; + power?: number | null; + power_down_count?: number | null; + power_down_count_updated_at?: string | null; + power_limit?: number | null; + power_limit_hps_mode?: number; + power_limit_should_be?: number | null; + power_limit_should_be_updated_at?: string | null; + power_limit_updated_at?: string | null; + power_updated_at?: string | null; + pulse_counter_kwh?: number | null; + pulse_counter_kwh_updated_at?: string | null; + rls_grid_id?: number | null; + rls_organization_id?: number | null; + should_be_on?: boolean | null; + should_be_on_updated_at?: string | null; + version?: string | null; + voltage?: number | null; + voltage_updated_at?: string | null; + watchdog_last_run_at?: string | null; + watchdog_session?: string | null; + }; + Relationships: [ + { + foreignKeyName: 'FK_04e1a755d5b760f681e5205557d'; + columns: ['dcu_id']; + isOneToOne: false; + referencedRelation: 'dcus'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_5606d6ec5ab568377509edc5267'; + columns: ['last_encountered_issue_id']; + isOneToOne: true; + referencedRelation: 'issues'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_86d4557a79e374b5c55cb3b66d8'; + columns: ['last_metering_hardware_install_session_id']; + isOneToOne: true; + referencedRelation: 'metering_hardware_install_sessions'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_a11871575f7ddb95e567d842bc6'; + columns: ['pole_id']; + isOneToOne: false; + referencedRelation: 'poles'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_d281c6e5f391b8de5b221032da6'; + columns: ['connection_id']; + isOneToOne: false; + referencedRelation: 'connections'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'meters_device_id_fkey'; + columns: ['device_id']; + isOneToOne: true; + referencedRelation: 'devices'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'meters_rls_grid_id_fkey'; + columns: ['rls_grid_id']; + isOneToOne: false; + referencedRelation: 'grids'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'meters_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + ]; + }; + migrations: { + Row: { + id: number; + name: string; + timestamp: number; + }; + Insert: { + id?: number; + name: string; + timestamp: number; + }; + Update: { + id?: number; + name?: string; + timestamp?: number; + }; + Relationships: []; + }; + mppts: { + Row: { + azimuth: number | null; + created_at: string; + deleted_at: string | null; + external_id: string; + external_reference: string; + external_system: Database['public']['Enums']['external_system_enum']; + grid_id: number | null; + id: number; + installed_at: string; + kw: number | null; + mppt_type: Database['public']['Enums']['mppt_type_enum']; + rls_organization_id: number | null; + tilt: number | null; + }; + Insert: { + azimuth?: number | null; + created_at?: string; + deleted_at?: string | null; + external_id: string; + external_reference: string; + external_system?: Database['public']['Enums']['external_system_enum']; + grid_id?: number | null; + id?: number; + installed_at?: string; + kw?: number | null; + mppt_type?: Database['public']['Enums']['mppt_type_enum']; + rls_organization_id?: number | null; + tilt?: number | null; + }; + Update: { + azimuth?: number | null; + created_at?: string; + deleted_at?: string | null; + external_id?: string; + external_reference?: string; + external_system?: Database['public']['Enums']['external_system_enum']; + grid_id?: number | null; + id?: number; + installed_at?: string; + kw?: number | null; + mppt_type?: Database['public']['Enums']['mppt_type_enum']; + rls_organization_id?: number | null; + tilt?: number | null; + }; + Relationships: [ + { + foreignKeyName: 'FK_0a681c1d6d0a67331bbc6956427'; + columns: ['grid_id']; + isOneToOne: false; + referencedRelation: 'grids'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'mppts_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + ]; + }; + notes: { + Row: { + author_id: number | null; + connection_id: number | null; + created_at: string; + customer_id: number | null; + id: number; + message: string | null; + meter_id: number | null; + rls_organization_id: number | null; + }; + Insert: { + author_id?: number | null; + connection_id?: number | null; + created_at?: string; + customer_id?: number | null; + id?: number; + message?: string | null; + meter_id?: number | null; + rls_organization_id?: number | null; + }; + Update: { + author_id?: number | null; + connection_id?: number | null; + created_at?: string; + customer_id?: number | null; + id?: number; + message?: string | null; + meter_id?: number | null; + rls_organization_id?: number | null; + }; + Relationships: [ + { + foreignKeyName: 'FK_35b89a50cb9203dccff44136519'; + columns: ['author_id']; + isOneToOne: false; + referencedRelation: 'accounts'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_5ec9536f852f1923097ba1ecdac'; + columns: ['meter_id']; + isOneToOne: false; + referencedRelation: 'meters'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_5ec9536f852f1923097ba1ecdac'; + columns: ['meter_id']; + isOneToOne: false; + referencedRelation: 'meters_with_account_and_statuses'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_e04c36c14bc9f01f84cd7655b68'; + columns: ['customer_id']; + isOneToOne: false; + referencedRelation: 'customers'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_e04c36c14bc9f01f84cd7655b68'; + columns: ['customer_id']; + isOneToOne: false; + referencedRelation: 'customers_with_account'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_f1a5ea7b77453030e2a1df27479'; + columns: ['connection_id']; + isOneToOne: false; + referencedRelation: 'connections'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'notes_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + ]; + }; + notification_parameters: { + Row: { + created_at: string; + id: number; + parameters: Json | null; + }; + Insert: { + created_at?: string; + id?: number; + parameters?: Json | null; + }; + Update: { + created_at?: string; + id?: number; + parameters?: Json | null; + }; + Relationships: []; + }; + notifications: { + Row: { + account_id: number | null; + carrier_external_system: + | Database['public']['Enums']['external_system_enum'] + | null; + chat_id: string | null; + connector_external_system: + | Database['public']['Enums']['external_system_enum'] + | null; + created_at: string; + email: string | null; + external_reference: string | null; + grid_id: number | null; + id: number; + lock_session: string | null; + message: string | null; + notification_parameter_id: number | null; + notification_status: Database['public']['Enums']['notification_status_enum']; + notification_type: Database['public']['Enums']['notification_type_enum']; + organization_id: number | null; + phone: string | null; + subject: string | null; + thread_id: string | null; + }; + Insert: { + account_id?: number | null; + carrier_external_system?: + | Database['public']['Enums']['external_system_enum'] + | null; + chat_id?: string | null; + connector_external_system?: + | Database['public']['Enums']['external_system_enum'] + | null; + created_at?: string; + email?: string | null; + external_reference?: string | null; + grid_id?: number | null; + id?: number; + lock_session?: string | null; + message?: string | null; + notification_parameter_id?: number | null; + notification_status: Database['public']['Enums']['notification_status_enum']; + notification_type: Database['public']['Enums']['notification_type_enum']; + organization_id?: number | null; + phone?: string | null; + subject?: string | null; + thread_id?: string | null; + }; + Update: { + account_id?: number | null; + carrier_external_system?: + | Database['public']['Enums']['external_system_enum'] + | null; + chat_id?: string | null; + connector_external_system?: + | Database['public']['Enums']['external_system_enum'] + | null; + created_at?: string; + email?: string | null; + external_reference?: string | null; + grid_id?: number | null; + id?: number; + lock_session?: string | null; + message?: string | null; + notification_parameter_id?: number | null; + notification_status?: Database['public']['Enums']['notification_status_enum']; + notification_type?: Database['public']['Enums']['notification_type_enum']; + organization_id?: number | null; + phone?: string | null; + subject?: string | null; + thread_id?: string | null; + }; + Relationships: [ + { + foreignKeyName: 'FK_982f77636cb1da8e76745949530'; + columns: ['notification_parameter_id']; + isOneToOne: false; + referencedRelation: 'notification_parameters'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_a1ec3b4b4f2017665b534e60256'; + columns: ['account_id']; + isOneToOne: false; + referencedRelation: 'accounts'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_c1053eec8005016c7d9febdc484'; + columns: ['grid_id']; + isOneToOne: false; + referencedRelation: 'grids'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_cb7b1fb018b296f2107e998b2ff'; + columns: ['organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + ]; + }; + orders: { + Row: { + amount: number; + author_id: number | null; + created_at: string; + currency: Database['public']['Enums']['currency_enum']; + directive_id: number | null; + external_reference: string | null; + external_system: + | Database['public']['Enums']['external_system_enum'] + | null; + historical_grid_id: number | null; + id: number; + lock_session: string | null; + lorawan_directive_id: number | null; + meta_author_id: number | null; + meta_author_name: string | null; + meta_author_type: + | Database['public']['Enums']['account_type_enum'] + | null; + meta_is_hidden_from_reporting: boolean | null; + meta_order_type: + | Database['public']['Enums']['order_type_enum'] + | null; + meta_receiver_id: number | null; + meta_receiver_id_part_2: number | null; + meta_receiver_name: string | null; + meta_receiver_name_part_2: string | null; + meta_receiver_type: + | Database['public']['Enums']['order_actor_type_enum'] + | null; + meta_sender_id: number | null; + meta_sender_name: string | null; + meta_sender_name_part_2: string | null; + meta_sender_type: + | Database['public']['Enums']['order_actor_type_enum'] + | null; + meter_credit_transfer_id: number | null; + order_status: Database['public']['Enums']['order_status_enum']; + payment_channel: + | Database['public']['Enums']['payment_channel_enum'] + | null; + payment_method: + | Database['public']['Enums']['payment_method_enum'] + | null; + receiver_wallet_id: number | null; + rls_organization_id: number | null; + sender_wallet_id: number | null; + tariff: number; + tariff_type: Database['public']['Enums']['meter_type_enum'] | null; + updated_at: string; + ussd_session_id: number | null; + }; + Insert: { + amount: number; + author_id?: number | null; + created_at?: string; + currency: Database['public']['Enums']['currency_enum']; + directive_id?: number | null; + external_reference?: string | null; + external_system?: + | Database['public']['Enums']['external_system_enum'] + | null; + historical_grid_id?: number | null; + id?: number; + lock_session?: string | null; + lorawan_directive_id?: number | null; + meta_author_id?: number | null; + meta_author_name?: string | null; + meta_author_type?: + | Database['public']['Enums']['account_type_enum'] + | null; + meta_is_hidden_from_reporting?: boolean | null; + meta_order_type?: + | Database['public']['Enums']['order_type_enum'] + | null; + meta_receiver_id?: number | null; + meta_receiver_id_part_2?: number | null; + meta_receiver_name?: string | null; + meta_receiver_name_part_2?: string | null; + meta_receiver_type?: + | Database['public']['Enums']['order_actor_type_enum'] + | null; + meta_sender_id?: number | null; + meta_sender_name?: string | null; + meta_sender_name_part_2?: string | null; + meta_sender_type?: + | Database['public']['Enums']['order_actor_type_enum'] + | null; + meter_credit_transfer_id?: number | null; + order_status?: Database['public']['Enums']['order_status_enum']; + payment_channel?: + | Database['public']['Enums']['payment_channel_enum'] + | null; + payment_method?: + | Database['public']['Enums']['payment_method_enum'] + | null; + receiver_wallet_id?: number | null; + rls_organization_id?: number | null; + sender_wallet_id?: number | null; + tariff?: number; + tariff_type?: Database['public']['Enums']['meter_type_enum'] | null; + updated_at?: string; + ussd_session_id?: number | null; + }; + Update: { + amount?: number; + author_id?: number | null; + created_at?: string; + currency?: Database['public']['Enums']['currency_enum']; + directive_id?: number | null; + external_reference?: string | null; + external_system?: + | Database['public']['Enums']['external_system_enum'] + | null; + historical_grid_id?: number | null; + id?: number; + lock_session?: string | null; + lorawan_directive_id?: number | null; + meta_author_id?: number | null; + meta_author_name?: string | null; + meta_author_type?: + | Database['public']['Enums']['account_type_enum'] + | null; + meta_is_hidden_from_reporting?: boolean | null; + meta_order_type?: + | Database['public']['Enums']['order_type_enum'] + | null; + meta_receiver_id?: number | null; + meta_receiver_id_part_2?: number | null; + meta_receiver_name?: string | null; + meta_receiver_name_part_2?: string | null; + meta_receiver_type?: + | Database['public']['Enums']['order_actor_type_enum'] + | null; + meta_sender_id?: number | null; + meta_sender_name?: string | null; + meta_sender_name_part_2?: string | null; + meta_sender_type?: + | Database['public']['Enums']['order_actor_type_enum'] + | null; + meter_credit_transfer_id?: number | null; + order_status?: Database['public']['Enums']['order_status_enum']; + payment_channel?: + | Database['public']['Enums']['payment_channel_enum'] + | null; + payment_method?: + | Database['public']['Enums']['payment_method_enum'] + | null; + receiver_wallet_id?: number | null; + rls_organization_id?: number | null; + sender_wallet_id?: number | null; + tariff?: number; + tariff_type?: Database['public']['Enums']['meter_type_enum'] | null; + updated_at?: string; + ussd_session_id?: number | null; + }; + Relationships: [ + { + foreignKeyName: 'FK_093ca3525311b8a12f6cf6b1c9b'; + columns: ['directive_id']; + isOneToOne: true; + referencedRelation: 'directives'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_0b03d1bc1cff784570333129e63'; + columns: ['historical_grid_id']; + isOneToOne: false; + referencedRelation: 'grids'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_576b0e9af0bec469bff33b965aa'; + columns: ['receiver_wallet_id']; + isOneToOne: false; + referencedRelation: 'wallets'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_60816230893327daacc86ab41c8'; + columns: ['author_id']; + isOneToOne: false; + referencedRelation: 'accounts'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_8f12fe9c7b078122adcae80375d'; + columns: ['sender_wallet_id']; + isOneToOne: false; + referencedRelation: 'wallets'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_92081072063eaf51300ab6c267d'; + columns: ['ussd_session_id']; + isOneToOne: true; + referencedRelation: 'ussd_sessions'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_a53c58bdb5ae0193f17497c81b4'; + columns: ['meter_credit_transfer_id']; + isOneToOne: true; + referencedRelation: 'meter_credit_transfers'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'orders_lorawan_directive_id_fkey'; + columns: ['lorawan_directive_id']; + isOneToOne: false; + referencedRelation: 'lorawan_directives'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'orders_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + ]; + }; + organizations: { + Row: { + address: string | null; + created_at: string; + deleted_at: string | null; + developer_group_telegram_chat_id: string | null; + email: string | null; + epicollect_contract_last_sync_at: string | null; + epicollect_contract_survey_client_id: string | null; + epicollect_contract_survey_secret: string | null; + epicollect_contract_survey_slug: string | null; + formal_name: string | null; + id: number; + name: string; + organization_type: Database['public']['Enums']['organization_type_enum']; + pd_hero_google_drive_folder_id: string | null; + phone: string | null; + }; + Insert: { + address?: string | null; + created_at?: string; + deleted_at?: string | null; + developer_group_telegram_chat_id?: string | null; + email?: string | null; + epicollect_contract_last_sync_at?: string | null; + epicollect_contract_survey_client_id?: string | null; + epicollect_contract_survey_secret?: string | null; + epicollect_contract_survey_slug?: string | null; + formal_name?: string | null; + id?: number; + name: string; + organization_type?: Database['public']['Enums']['organization_type_enum']; + pd_hero_google_drive_folder_id?: string | null; + phone?: string | null; + }; + Update: { + address?: string | null; + created_at?: string; + deleted_at?: string | null; + developer_group_telegram_chat_id?: string | null; + email?: string | null; + epicollect_contract_last_sync_at?: string | null; + epicollect_contract_survey_client_id?: string | null; + epicollect_contract_survey_secret?: string | null; + epicollect_contract_survey_slug?: string | null; + formal_name?: string | null; + id?: number; + name?: string; + organization_type?: Database['public']['Enums']['organization_type_enum']; + pd_hero_google_drive_folder_id?: string | null; + phone?: string | null; + }; + Relationships: []; + }; + payouts: { + Row: { + approved_amount: number | null; + approved_by_account_id: number | null; + bank_account_id: number | null; + created_at: string; + details: Json | null; + draft_link: string | null; + ended_at: string; + external_reference: string | null; + external_system: + | Database['public']['Enums']['external_system_enum'] + | null; + grid_id: number; + id: number; + payout_status: Database['public']['Enums']['payout_status_enum']; + proposed_amount: number; + started_at: string; + }; + Insert: { + approved_amount?: number | null; + approved_by_account_id?: number | null; + bank_account_id?: number | null; + created_at?: string; + details?: Json | null; + draft_link?: string | null; + ended_at: string; + external_reference?: string | null; + external_system?: + | Database['public']['Enums']['external_system_enum'] + | null; + grid_id: number; + id?: number; + payout_status: Database['public']['Enums']['payout_status_enum']; + proposed_amount: number; + started_at: string; + }; + Update: { + approved_amount?: number | null; + approved_by_account_id?: number | null; + bank_account_id?: number | null; + created_at?: string; + details?: Json | null; + draft_link?: string | null; + ended_at?: string; + external_reference?: string | null; + external_system?: + | Database['public']['Enums']['external_system_enum'] + | null; + grid_id?: number; + id?: number; + payout_status?: Database['public']['Enums']['payout_status_enum']; + proposed_amount?: number; + started_at?: string; + }; + Relationships: [ + { + foreignKeyName: 'FK_7796c1df57eeefa9173325627c4'; + columns: ['bank_account_id']; + isOneToOne: false; + referencedRelation: 'bank_accounts'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_e2e6de2a3c003b36f840829079a'; + columns: ['approved_by_account_id']; + isOneToOne: false; + referencedRelation: 'accounts'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_e8bc1a446b4df957b8d37af1cd0'; + columns: ['grid_id']; + isOneToOne: false; + referencedRelation: 'grids'; + referencedColumns: ['id']; + }, + ]; + }; + pd_action_templates: { + Row: { + created_at: string; + depends_on: Json | null; + id: number; + key: string | null; + name: string; + parameters: Json; + pd_action_type: Database['public']['Enums']['pd_action_type_enum']; + pd_document_template_id: number | null; + pd_flow_template_id: number; + }; + Insert: { + created_at?: string; + depends_on?: Json | null; + id?: number; + key?: string | null; + name: string; + parameters?: Json; + pd_action_type: Database['public']['Enums']['pd_action_type_enum']; + pd_document_template_id?: number | null; + pd_flow_template_id: number; + }; + Update: { + created_at?: string; + depends_on?: Json | null; + id?: number; + key?: string | null; + name?: string; + parameters?: Json; + pd_action_type?: Database['public']['Enums']['pd_action_type_enum']; + pd_document_template_id?: number | null; + pd_flow_template_id?: number; + }; + Relationships: [ + { + foreignKeyName: 'pd_action_templates_pd_document_template_id_fkey'; + columns: ['pd_document_template_id']; + isOneToOne: false; + referencedRelation: 'pd_document_templates'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'pd_action_templates_pd_flow_template_id_fkey'; + columns: ['pd_flow_template_id']; + isOneToOne: false; + referencedRelation: 'pd_flow_templates'; + referencedColumns: ['id']; + }, + ]; + }; + pd_actions: { + Row: { + created_at: string; + depends_on: Json | null; + id: number; + key: string | null; + latest_pd_document_id: number | null; + name: string; + parameters: Json; + pd_action_status: Database['public']['Enums']['pd_action_status_enum']; + pd_action_template_id: number | null; + pd_action_type: Database['public']['Enums']['pd_action_type_enum']; + pd_flow_id: number | null; + pd_section_id: number | null; + }; + Insert: { + created_at?: string; + depends_on?: Json | null; + id?: number; + key?: string | null; + latest_pd_document_id?: number | null; + name: string; + parameters?: Json; + pd_action_status?: Database['public']['Enums']['pd_action_status_enum']; + pd_action_template_id?: number | null; + pd_action_type: Database['public']['Enums']['pd_action_type_enum']; + pd_flow_id?: number | null; + pd_section_id?: number | null; + }; + Update: { + created_at?: string; + depends_on?: Json | null; + id?: number; + key?: string | null; + latest_pd_document_id?: number | null; + name?: string; + parameters?: Json; + pd_action_status?: Database['public']['Enums']['pd_action_status_enum']; + pd_action_template_id?: number | null; + pd_action_type?: Database['public']['Enums']['pd_action_type_enum']; + pd_flow_id?: number | null; + pd_section_id?: number | null; + }; + Relationships: [ + { + foreignKeyName: 'pd_actions_latest_pd_document_id_fkey'; + columns: ['latest_pd_document_id']; + isOneToOne: false; + referencedRelation: 'pd_documents'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'pd_actions_pd_action_template_id_fkey'; + columns: ['pd_action_template_id']; + isOneToOne: false; + referencedRelation: 'pd_action_templates'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'pd_actions_pd_flow_id_fkey'; + columns: ['pd_flow_id']; + isOneToOne: false; + referencedRelation: 'pd_flows'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'pd_actions_pd_section_id_fkey'; + columns: ['pd_section_id']; + isOneToOne: false; + referencedRelation: 'pd_actions'; + referencedColumns: ['id']; + }, + ]; + }; + pd_audits: { + Row: { + author_id: number | null; + created_at: string; + id: number; + message: string | null; + pd_action_id: number | null; + }; + Insert: { + author_id?: number | null; + created_at?: string; + id?: number; + message?: string | null; + pd_action_id?: number | null; + }; + Update: { + author_id?: number | null; + created_at?: string; + id?: number; + message?: string | null; + pd_action_id?: number | null; + }; + Relationships: [ + { + foreignKeyName: 'pd_audits_author_id_fkey'; + columns: ['author_id']; + isOneToOne: false; + referencedRelation: 'accounts'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'pd_audits_pd_action_id_fkey'; + columns: ['pd_action_id']; + isOneToOne: false; + referencedRelation: 'pd_actions'; + referencedColumns: ['id']; + }, + ]; + }; + pd_document_templates: { + Row: { + created_at: string; + google_drive_template_id: string; + id: number; + parameters: Json; + pd_document_type: + | Database['public']['Enums']['pd_document_type_enum'] + | null; + title: string | null; + }; + Insert: { + created_at?: string; + google_drive_template_id: string; + id?: number; + parameters?: Json; + pd_document_type?: + | Database['public']['Enums']['pd_document_type_enum'] + | null; + title?: string | null; + }; + Update: { + created_at?: string; + google_drive_template_id?: string; + id?: number; + parameters?: Json; + pd_document_type?: + | Database['public']['Enums']['pd_document_type_enum'] + | null; + title?: string | null; + }; + Relationships: []; + }; + pd_documents: { + Row: { + created_at: string; + google_drive_document_id: string; + id: number; + pd_action_id: number | null; + pd_document_type: + | Database['public']['Enums']['pd_document_type_enum'] + | null; + title: string | null; + }; + Insert: { + created_at?: string; + google_drive_document_id: string; + id?: number; + pd_action_id?: number | null; + pd_document_type?: + | Database['public']['Enums']['pd_document_type_enum'] + | null; + title?: string | null; + }; + Update: { + created_at?: string; + google_drive_document_id?: string; + id?: number; + pd_action_id?: number | null; + pd_document_type?: + | Database['public']['Enums']['pd_document_type_enum'] + | null; + title?: string | null; + }; + Relationships: [ + { + foreignKeyName: 'pd_documents_pd_action_id_fkey'; + columns: ['pd_action_id']; + isOneToOne: false; + referencedRelation: 'pd_actions'; + referencedColumns: ['id']; + }, + ]; + }; + pd_flow_templates: { + Row: { + created_at: string; + id: number; + name: string; + parameters: Json; + }; + Insert: { + created_at?: string; + id?: number; + name: string; + parameters?: Json; + }; + Update: { + created_at?: string; + id?: number; + name?: string; + parameters?: Json; + }; + Relationships: []; + }; + pd_flows: { + Row: { + created_at: string; + google_folder_id: string | null; + id: number; + parameters: Json; + pd_flow_template_id: number; + title: string | null; + }; + Insert: { + created_at?: string; + google_folder_id?: string | null; + id?: number; + parameters?: Json; + pd_flow_template_id: number; + title?: string | null; + }; + Update: { + created_at?: string; + google_folder_id?: string | null; + id?: number; + parameters?: Json; + pd_flow_template_id?: number; + title?: string | null; + }; + Relationships: [ + { + foreignKeyName: 'pd_flows_pd_flow_template_id_fkey'; + columns: ['pd_flow_template_id']; + isOneToOne: false; + referencedRelation: 'pd_flow_templates'; + referencedColumns: ['id']; + }, + ]; + }; + pd_section_templates: { + Row: { + created_at: string; + id: number; + title: string | null; + }; + Insert: { + created_at?: string; + id?: number; + title?: string | null; + }; + Update: { + created_at?: string; + id?: number; + title?: string | null; + }; + Relationships: []; + }; + pd_sections: { + Row: { + created_at: string; + id: number; + title: string | null; + }; + Insert: { + created_at?: string; + id?: number; + title?: string | null; + }; + Update: { + created_at?: string; + id?: number; + title?: string | null; + }; + Relationships: []; + }; + pd_site_submissions: { + Row: { + author_email: string; + author_full_name: string; + author_organization_id: number | null; + author_organization_name: string; + buildings_geo_flat: Json | null; + created_at: string; + deleted_at: string | null; + distribution_geo_flat: Json | null; + id: number; + location_geom: unknown; + meta_geo_flat: Json | null; + organization_id: number | null; + outline_geom: unknown; + poles_geo_flat: Json | null; + site_details: Json; + site_name: string; + }; + Insert: { + author_email: string; + author_full_name: string; + author_organization_id?: number | null; + author_organization_name: string; + buildings_geo_flat?: Json | null; + created_at?: string; + deleted_at?: string | null; + distribution_geo_flat?: Json | null; + id?: number; + location_geom?: unknown; + meta_geo_flat?: Json | null; + organization_id?: number | null; + outline_geom?: unknown; + poles_geo_flat?: Json | null; + site_details: Json; + site_name: string; + }; + Update: { + author_email?: string; + author_full_name?: string; + author_organization_id?: number | null; + author_organization_name?: string; + buildings_geo_flat?: Json | null; + created_at?: string; + deleted_at?: string | null; + distribution_geo_flat?: Json | null; + id?: number; + location_geom?: unknown; + meta_geo_flat?: Json | null; + organization_id?: number | null; + outline_geom?: unknown; + poles_geo_flat?: Json | null; + site_details?: Json; + site_name?: string; + }; + Relationships: [ + { + foreignKeyName: 'pd_site_submissions_organization_id_fkey'; + columns: ['organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + ]; + }; + pd_sites: { + Row: { + created_at: string; + deleted_at: string | null; + id: number; + location_geom: unknown; + name: string; + operations_grid_id: number | null; + organization_id: number | null; + outline_geom: unknown; + pd_flow_id: number | null; + }; + Insert: { + created_at?: string; + deleted_at?: string | null; + id?: number; + location_geom?: unknown; + name: string; + operations_grid_id?: number | null; + organization_id?: number | null; + outline_geom?: unknown; + pd_flow_id?: number | null; + }; + Update: { + created_at?: string; + deleted_at?: string | null; + id?: number; + location_geom?: unknown; + name?: string; + operations_grid_id?: number | null; + organization_id?: number | null; + outline_geom?: unknown; + pd_flow_id?: number | null; + }; + Relationships: [ + { + foreignKeyName: 'pd_sites_operations_grid_id_fkey'; + columns: ['operations_grid_id']; + isOneToOne: false; + referencedRelation: 'grids'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'pd_sites_organization_id_fkey'; + columns: ['organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'pd_sites_pd_flow_id_fkey'; + columns: ['pd_flow_id']; + isOneToOne: false; + referencedRelation: 'pd_flows'; + referencedColumns: ['id']; + }, + ]; + }; + poles: { + Row: { + created_at: string; + external_reference: string; + grid_id: number | null; + id: number; + is_virtual: boolean; + location_accuracy: number | null; + location_geom: unknown; + nickname: string | null; + rls_organization_id: number | null; + }; + Insert: { + created_at?: string; + external_reference: string; + grid_id?: number | null; + id?: number; + is_virtual?: boolean; + location_accuracy?: number | null; + location_geom: unknown; + nickname?: string | null; + rls_organization_id?: number | null; + }; + Update: { + created_at?: string; + external_reference?: string; + grid_id?: number | null; + id?: number; + is_virtual?: boolean; + location_accuracy?: number | null; + location_geom?: unknown; + nickname?: string | null; + rls_organization_id?: number | null; + }; + Relationships: [ + { + foreignKeyName: 'FK_2c21d74185234b0dd39588a4d36'; + columns: ['grid_id']; + isOneToOne: false; + referencedRelation: 'grids'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'poles_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + ]; + }; + routers: { + Row: { + created_at: string; + deleted_at: string | null; + external_reference: string; + external_system: Database['public']['Enums']['external_system_enum']; + grid_id: number | null; + id: number; + is_online: boolean; + is_online_updated_at: string | null; + rls_organization_id: number | null; + }; + Insert: { + created_at?: string; + deleted_at?: string | null; + external_reference: string; + external_system?: Database['public']['Enums']['external_system_enum']; + grid_id?: number | null; + id?: number; + is_online?: boolean; + is_online_updated_at?: string | null; + rls_organization_id?: number | null; + }; + Update: { + created_at?: string; + deleted_at?: string | null; + external_reference?: string; + external_system?: Database['public']['Enums']['external_system_enum']; + grid_id?: number | null; + id?: number; + is_online?: boolean; + is_online_updated_at?: string | null; + rls_organization_id?: number | null; + }; + Relationships: [ + { + foreignKeyName: 'FK_3c3ddc58369a9a8147776e39f40'; + columns: ['grid_id']; + isOneToOne: false; + referencedRelation: 'grids'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'routers_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + ]; + }; + solcast_cache: { + Row: { + azimuth: number; + capacity_kwp: number; + created_at: string; + id: number; + install_date: string; + latitude: number; + longitude: number; + request_type: Database['public']['Enums']['solcast_cache_request_type_enum']; + response: string; + tilt: number; + }; + Insert: { + azimuth: number; + capacity_kwp: number; + created_at?: string; + id?: number; + install_date: string; + latitude: number; + longitude: number; + request_type: Database['public']['Enums']['solcast_cache_request_type_enum']; + response: string; + tilt: number; + }; + Update: { + azimuth?: number; + capacity_kwp?: number; + created_at?: string; + id?: number; + install_date?: string; + latitude?: number; + longitude?: number; + request_type?: Database['public']['Enums']['solcast_cache_request_type_enum']; + response?: string; + tilt?: number; + }; + Relationships: []; + }; + transactions: { + Row: { + amount: number; + balance_after: number; + balance_before: number; + created_at: string; + id: number; + order_id: number | null; + rls_organization_id: number | null; + transaction_status: Database['public']['Enums']['transaction_status_enum']; + wallet_id: number | null; + }; + Insert: { + amount: number; + balance_after?: number; + balance_before?: number; + created_at?: string; + id?: number; + order_id?: number | null; + rls_organization_id?: number | null; + transaction_status: Database['public']['Enums']['transaction_status_enum']; + wallet_id?: number | null; + }; + Update: { + amount?: number; + balance_after?: number; + balance_before?: number; + created_at?: string; + id?: number; + order_id?: number | null; + rls_organization_id?: number | null; + transaction_status?: Database['public']['Enums']['transaction_status_enum']; + wallet_id?: number | null; + }; + Relationships: [ + { + foreignKeyName: 'FK_0b171330be0cb621f8d73b87a9e'; + columns: ['wallet_id']; + isOneToOne: false; + referencedRelation: 'wallets'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_3cb0558ed36997f1d9ecc1118e7'; + columns: ['order_id']; + isOneToOne: false; + referencedRelation: 'orders'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'transactions_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + ]; + }; + ussd_session_hops: { + Row: { + created_at: string; + id: number; + network_code: string | null; + phone: string | null; + service_code: string | null; + text: string | null; + ussd_session_id: number | null; + }; + Insert: { + created_at?: string; + id?: number; + network_code?: string | null; + phone?: string | null; + service_code?: string | null; + text?: string | null; + ussd_session_id?: number | null; + }; + Update: { + created_at?: string; + id?: number; + network_code?: string | null; + phone?: string | null; + service_code?: string | null; + text?: string | null; + ussd_session_id?: number | null; + }; + Relationships: [ + { + foreignKeyName: 'FK_bf89299bd6534528adad645b7a5'; + columns: ['ussd_session_id']; + isOneToOne: false; + referencedRelation: 'ussd_sessions'; + referencedColumns: ['id']; + }, + ]; + }; + ussd_sessions: { + Row: { + account_id: number | null; + amount: number | null; + bank_id: number | null; + created_at: string; + external_reference: string | null; + external_system: Database['public']['Enums']['external_system_enum']; + id: number; + is_using_other_option: boolean; + meter_id: number | null; + phone: string; + }; + Insert: { + account_id?: number | null; + amount?: number | null; + bank_id?: number | null; + created_at?: string; + external_reference?: string | null; + external_system: Database['public']['Enums']['external_system_enum']; + id?: number; + is_using_other_option?: boolean; + meter_id?: number | null; + phone: string; + }; + Update: { + account_id?: number | null; + amount?: number | null; + bank_id?: number | null; + created_at?: string; + external_reference?: string | null; + external_system?: Database['public']['Enums']['external_system_enum']; + id?: number; + is_using_other_option?: boolean; + meter_id?: number | null; + phone?: string; + }; + Relationships: [ + { + foreignKeyName: 'FK_3a2606011b8d9f1de07de2bf0a8'; + columns: ['meter_id']; + isOneToOne: false; + referencedRelation: 'meters'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_3a2606011b8d9f1de07de2bf0a8'; + columns: ['meter_id']; + isOneToOne: false; + referencedRelation: 'meters_with_account_and_statuses'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_4cad92626a62976ac0b49244d2e'; + columns: ['bank_id']; + isOneToOne: false; + referencedRelation: 'banks'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_ede50d58fa445bfa012135249fd'; + columns: ['account_id']; + isOneToOne: false; + referencedRelation: 'accounts'; + referencedColumns: ['id']; + }, + ]; + }; + wallets: { + Row: { + agent_id: number | null; + balance: number; + balance_updated_at: string | null; + connection_id: number | null; + created_at: string; + customer_id: number | null; + goldring_migration_id: number | null; + id: number; + identifier: string | null; + lock_session: string | null; + meter_id: number | null; + organization_id: number | null; + rls_organization_id: number | null; + wallet_type: Database['public']['Enums']['wallet_type_enum']; + }; + Insert: { + agent_id?: number | null; + balance?: number; + balance_updated_at?: string | null; + connection_id?: number | null; + created_at?: string; + customer_id?: number | null; + goldring_migration_id?: number | null; + id?: number; + identifier?: string | null; + lock_session?: string | null; + meter_id?: number | null; + organization_id?: number | null; + rls_organization_id?: number | null; + wallet_type?: Database['public']['Enums']['wallet_type_enum']; + }; + Update: { + agent_id?: number | null; + balance?: number; + balance_updated_at?: string | null; + connection_id?: number | null; + created_at?: string; + customer_id?: number | null; + goldring_migration_id?: number | null; + id?: number; + identifier?: string | null; + lock_session?: string | null; + meter_id?: number | null; + organization_id?: number | null; + rls_organization_id?: number | null; + wallet_type?: Database['public']['Enums']['wallet_type_enum']; + }; + Relationships: [ + { + foreignKeyName: 'FK_6580899a2293de27787376887fa'; + columns: ['customer_id']; + isOneToOne: true; + referencedRelation: 'customers'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_6580899a2293de27787376887fa'; + columns: ['customer_id']; + isOneToOne: true; + referencedRelation: 'customers_with_account'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_8db1f4e4f8122bd25d50ad96b26'; + columns: ['meter_id']; + isOneToOne: true; + referencedRelation: 'meters'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_8db1f4e4f8122bd25d50ad96b26'; + columns: ['meter_id']; + isOneToOne: true; + referencedRelation: 'meters_with_account_and_statuses'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_e63e504d8e35ef37a2c56b75eb9'; + columns: ['connection_id']; + isOneToOne: true; + referencedRelation: 'connections'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_f499c61c6d6a0ac3f794d966edc'; + columns: ['organization_id']; + isOneToOne: true; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_f5782e05e8688f0cfbb5c4a52ce'; + columns: ['agent_id']; + isOneToOne: true; + referencedRelation: 'agents'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_f5782e05e8688f0cfbb5c4a52ce'; + columns: ['agent_id']; + isOneToOne: true; + referencedRelation: 'agents_with_account'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'wallets_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + ]; + }; + }; + Views: { + agents_with_account: { + Row: { + account_id: number | null; + created_at: string | null; + deleted_at: string | null; + full_name: string | null; + grid_id: number | null; + id: number | null; + phone: string | null; + }; + Relationships: [ + { + foreignKeyName: 'FK_9bb7cb7efc780ec4d3dec34354f'; + columns: ['grid_id']; + isOneToOne: false; + referencedRelation: 'grids'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_df520decc9e003a843d8edd9867'; + columns: ['account_id']; + isOneToOne: true; + referencedRelation: 'accounts'; + referencedColumns: ['id']; + }, + ]; + }; + batch_commands: { + Row: { + created_at: string | null; + dcu_external_reference: string | null; + dcu_id: number | null; + directive_batch_execution_id: number | null; + directive_error: + | Database['public']['Enums']['directive_error_enum'] + | null; + directive_status: + | Database['public']['Enums']['directive_status_enum'] + | null; + directive_type: + | Database['public']['Enums']['directive_type_enum'] + | null; + id: number | null; + latitude: number | null; + location_geom: unknown; + longitude: number | null; + meter_external_reference: string | null; + meter_id: number | null; + meter_type: Database['public']['Enums']['meter_type_enum'] | null; + }; + Relationships: []; + }; + customers_with_account: { + Row: { + account_id: number | null; + created_at: string | null; + deleted_at: string | null; + full_name: string | null; + gender: Database['public']['Enums']['gender_enum'] | null; + generator_owned: + | Database['public']['Enums']['generator_type_enum'] + | null; + grid_id: number | null; + has_fully_paid_connection_fees: boolean | null; + id: number | null; + is_hidden_from_reporting: boolean | null; + latitude: number | null; + lives_primarily_in_the_community: boolean | null; + longitude: number | null; + meter: string | null; + phone: string | null; + total_connection_fee: number | null; + total_connection_paid: number | null; + }; + Relationships: [ + { + foreignKeyName: 'FK_51a88ee1d4fceb047e7cfda3baa'; + columns: ['grid_id']; + isOneToOne: false; + referencedRelation: 'grids'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_ebcc29963874e55053e8ee80be5'; + columns: ['account_id']; + isOneToOne: true; + referencedRelation: 'accounts'; + referencedColumns: ['id']; + }, + ]; + }; + meters_with_account_and_statuses: { + Row: { + balance: number | null; + balance_updated_at: string | null; + communication_protocol: + | Database['public']['Enums']['communication_protocol_enum'] + | null; + connection_id: number | null; + coord_accuracy: number | null; + created_at: string | null; + current_special_status: + | Database['public']['Enums']['directive_special_status_enum'] + | null; + dcu_id: number | null; + decoder_key: string | null; + deleted_at: string | null; + device_id: number | null; + external_reference: string | null; + external_system: + | Database['public']['Enums']['external_system_enum'] + | null; + full_name: string | null; + grid_id: number | null; + id: number | null; + install_status: Database['public']['Enums']['mhi_status_enum'] | null; + is_cabin_meter: boolean | null; + is_hidden_from_reporting: boolean | null; + is_manual_mode_on: boolean | null; + is_manual_mode_on_updated_at: string | null; + is_on: boolean | null; + is_on_updated_at: string | null; + is_simulated: boolean | null; + is_starred: boolean | null; + is_test_mode_on: boolean | null; + issue_check_execution_session: string | null; + issue_check_last_run_at: string | null; + kwh_credit_available: number | null; + kwh_credit_available_updated_at: string | null; + kwh_tariff: number | null; + last_encountered_issue_id: number | null; + last_metering_hardware_install_session_id: number | null; + last_non_zero_consumption_at: string | null; + last_seen_at: string | null; + last_sts_token_issued_at: string | null; + latitude: number | null; + longitude: number | null; + meter_phase: Database['public']['Enums']['meter_phase_enum'] | null; + meter_type: Database['public']['Enums']['meter_type_enum'] | null; + nickname: string | null; + open_issue: Database['public']['Enums']['issue_type_enum'] | null; + phone: string | null; + pole_id: number | null; + power: number | null; + power_down_count: number | null; + power_down_count_updated_at: string | null; + power_limit: number | null; + power_limit_hps_mode: number | null; + power_limit_should_be: number | null; + power_limit_should_be_updated_at: string | null; + power_limit_updated_at: string | null; + power_updated_at: string | null; + pulse_counter_kwh: number | null; + rls_grid_id: number | null; + rls_organization_id: number | null; + should_be_on: boolean | null; + should_be_on_updated_at: string | null; + version: string | null; + voltage: number | null; + voltage_updated_at: string | null; + watchdog_last_run_at: string | null; + watchdog_session: string | null; + }; + Relationships: [ + { + foreignKeyName: 'FK_04e1a755d5b760f681e5205557d'; + columns: ['dcu_id']; + isOneToOne: false; + referencedRelation: 'dcus'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_51a88ee1d4fceb047e7cfda3baa'; + columns: ['grid_id']; + isOneToOne: false; + referencedRelation: 'grids'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_5606d6ec5ab568377509edc5267'; + columns: ['last_encountered_issue_id']; + isOneToOne: true; + referencedRelation: 'issues'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_86d4557a79e374b5c55cb3b66d8'; + columns: ['last_metering_hardware_install_session_id']; + isOneToOne: true; + referencedRelation: 'metering_hardware_install_sessions'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_a11871575f7ddb95e567d842bc6'; + columns: ['pole_id']; + isOneToOne: false; + referencedRelation: 'poles'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'FK_d281c6e5f391b8de5b221032da6'; + columns: ['connection_id']; + isOneToOne: false; + referencedRelation: 'connections'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'meters_device_id_fkey'; + columns: ['device_id']; + isOneToOne: true; + referencedRelation: 'devices'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'meters_rls_grid_id_fkey'; + columns: ['rls_grid_id']; + isOneToOne: false; + referencedRelation: 'grids'; + referencedColumns: ['id']; + }, + { + foreignKeyName: 'meters_rls_organization_id_fkey'; + columns: ['rls_organization_id']; + isOneToOne: false; + referencedRelation: 'organizations'; + referencedColumns: ['id']; + }, + ]; + }; + }; + Functions: { + find_energy_topup_revenue: { + Args: { end_date: string; grid_id: number; start_date: string }; + Returns: number; + }; + find_top_spenders: { + Args: { + end_date: string; + grid_id: number; + limit_count: number; + start_date: string; + }; + Returns: { + amount: number; + full_name: string; + id: number; + }[]; + }; + get_grid_status: { + Args: { grid_id: number }; + Returns: { + are_all_dcus_online: boolean; + are_all_dcus_under_high_load_threshold: boolean; + customer_count: number; + is_cabin_meter_credit_depleting: boolean; + is_fs_on: boolean; + is_hps_on: boolean; + }[]; + }; + lock_next_order: { Args: { uuid: string }; Returns: undefined }; + lock_next_order_and_wallets: { + Args: { uuid: string }; + Returns: { + id: number; + }[]; + }; + lock_next_pd_action: { Args: never; Returns: number }; + rls_check_if_lender: { Args: never; Returns: boolean }; + rls_check_if_nxt_member: { Args: never; Returns: boolean }; + rls_get_member_org_id: { Args: never; Returns: number }; + }; + Enums: { + account_type_enum: 'AGENT' | 'MEMBER' | 'CUSTOMER'; + communication_protocol_enum: 'CALIN_V1' | 'CALIN_V2' | 'CALIN_LORAWAN'; + currency_enum: 'USD' | 'NGN' | 'EUR'; + directive_direction_enum: 'UP' | 'DOWN'; + directive_error_enum: + | 'GRID_DOWN' + | 'DCU_OFFLINE' + | 'NO_METER' + | 'NO_DCU' + | 'NO_GRID' + | 'NO_CONNECTION' + | 'NO_CUSTOMER'; + directive_phase_enum: 'A' | 'B' | 'C'; + directive_special_status_enum: + | 'POWER_LIMIT_BREACHED' + | 'CREDIT_EXHAUSTED' + | 'REMOTE_SWITCHED_OFF' + | 'OVER_VOLTAGE' + | 'METER_NOT_ACTIVATED' + | 'TAMPER' + | 'LOW_VOLTAGE'; + directive_status_enum: + | 'INITIALISED' + | 'PENDING' + | 'SENT_TO_API' + | 'RECEIVED_BY_API' + | 'SENT_TO_DCU' + | 'RECEIVED_BY_DCU' + | 'SENT_TO_METER' + | 'RECEIVED_BY_METER' + | 'SUCCESSFUL' + | 'FAILED' + | 'IGNORED' + | 'CANCELLED' + | 'TIMED_OUT' + | 'UNKNOWN'; + directive_type_enum: + | 'ON' + | 'OFF' + | 'READ_VOLTAGE' + | 'PLS' + | 'PLR' + | 'CLEAR_TAMPER' + | 'READ_METER_VERSION' + | 'TOP_UP' + | 'READ_CURRENT_CREDIT' + | 'READ_CURRENT' + | 'READ_POWER' + | 'READ_SPECIAL_STATUS' + | 'CLEAR_CREDIT' + | 'READ_RELAY_STATUS' + | 'READ_POWER_DOWN_COUNT' + | 'READ_TIME' + | 'READ_DATE' + | 'READ_VOLTAGE_A' + | 'READ_VOLTAGE_B' + | 'READ_VOLTAGE_C' + | 'READ_POWER_A' + | 'READ_POWER_B' + | 'READ_POWER_C' + | 'READ_CURRENT_A' + | 'READ_CURRENT_B' + | 'READ_CURRENT_C' + | 'READ_TOTAL_ACTIVE_KWH' + | 'READ_STATUS' + | 'SEND_TOKEN' + | 'READ_FRAUD_STATUS' + | 'READ_REPORT' + | 'WRITE_DATE' + | 'WRITE_TIME' + | 'CLEAR_TAMPER_TOKEN' + | 'READ_REPORT_UP' + | 'READ_REPORT_DOWN' + | 'READ_CREDIT_DOWN' + | 'READ_CREDIT_UP' + | 'READ_VOLTAGE_UP' + | 'READ_VOLTAGE_DOWN' + | 'READ_POWER_UP' + | 'READ_POWER_DOWN' + | 'READ_CURRENT_UP' + | 'READ_CURRENT_DOWN' + | 'CLEAR_TAMPER_UP' + | 'CLEAR_TAMPER_DOWN' + | 'TOP_UP_DOWN' + | 'POWER_LIMIT_SET_UP' + | 'POWER_LIMIT_SET_DOWN' + | 'OPEN_RELAY_UP' + | 'OPEN_RELAY_DOWN' + | 'CLOSE_RELAY_UP' + | 'CLOSE_RELAY_DOWN' + | 'READ_VOLTAGE_A_UP' + | 'READ_VOLTAGE_A_DOWN' + | 'READ_POWER_A_UP' + | 'READ_POWER_A_DOWN' + | 'READ_CURRENT_A_UP' + | 'READ_CURRENT_A_DOWN' + | 'UNKNOWN' + | 'CLEAR_CREDIT_DOWN' + | 'TOP_UP_KWH' + | 'TOKEN_ACCEPTED' + | 'TOKEN_REJECTED' + | 'ON_OFF_ACCEPTED' + | 'ON_OFF_REJECTED'; + external_system_enum: + | 'STEAMACO' + | 'CALIN' + | 'SOLCAST' + | 'VICTRON' + | 'FLUTTERWAVE' + | 'AFRICASTALKING' + | 'JOTFORM' + | 'EPICOLLECT' + | 'JIRA' + | 'TELEGRAM' + | 'ZEROTIER' + | 'MAKE' + | 'FLOW_XO' + | 'SENDGRID' + | 'ACREL'; + fs_command_type_enum: 'ON' | 'OFF'; + gender_enum: 'MALE' | 'FEMALE'; + generator_type_enum: 'SMALL' | 'LARGE'; + id_document_type_enum: + | 'PASSPORT' + | 'NATIONAL_ID' + | 'DRIVING_LICENSE' + | 'VOTERS_CARD'; + issue_status_enum: 'OPEN' | 'CLOSED' | 'OVERRIDDEN'; + issue_type_enum: + | 'NO_COMMUNICATION' + | 'METER_NOT_ACTIVATED' + | 'TAMPER' + | 'POWER_LIMIT_BREACHED' + | 'OVER_VOLTAGE' + | 'LOW_VOLTAGE' + | 'POWER_LIMIT_BAD_CONFIGURATION' + | 'METER_STATE_BAD_CONFIGURATION' + | 'UNEXPECTED_POWER_LIMIT' + | 'UNEXPECTED_METER_STATUS' + | 'NO_CREDIT' + | 'NO_CONSUMPTION' + | 'NUMBER_OF_PHASES' + | 'VEBUS_STATE' + | 'VEBUS_ERROR' + | 'QUATTRO_TEMPERATURE_ALARM' + | 'QUATTRO_OVERLOAD_ALARM' + | 'HIGH_BATTERY_TEMPERATURE_ALARM' + | 'CELL_IMBALANCE_ALARM' + | 'HIGH_CHARGE_CURRENT_ALARM' + | 'HIGH_CHARGE_TEMPERATURE_ALARM' + | 'BATTERY_INTERNAL_FAILURE' + | 'BATTERY_CHARGE_BLOCKED_ALARM' + | 'BATTERY_DISCHARGE_BLOCKED_ALARM'; + member_type_enum: + | 'SUPERADMIN' + | 'ADMIN' + | 'PARTNER' + | 'FINANCE' + | 'DEVELOPER' + | 'MANAGER' + | 'SUPPORT' + | 'SERVICE' + | 'SALES' + | 'TECH'; + meter_commissioning_status_enum: + | 'PENDING' + | 'PROCESSING' + | 'SUCCESSFUL' + | 'FAILED'; + meter_credit_transfer_status_enum: + | 'PENDING' + | 'PROCESSING' + | 'SUCCESSFUL' + | 'FAILED'; + meter_interaction_status_enum: + | 'QUEUED' + | 'ABORTED' + | 'PROCESSING' + | 'SUCCESSFUL' + | 'FAILED' + | 'DEFERRED' + | 'SUSPENDED'; + meter_interaction_type_enum: + | 'READ_CREDIT' + | 'READ_POWER_LIMIT' + | 'READ_VOLTAGE' + | 'SET_POWER_LIMIT' + | 'TOP_UP' + | 'TURN_ON' + | 'TURN_OFF' + | 'READ_POWER' + | 'READ_CURRENT' + | 'CLEAR_CREDIT' + | 'CLEAR_TAMPER' + | 'READ_REPORT' + | 'JOIN_NETWORK' + | 'DELIVER_PREEXISTING_TOKEN' + | 'READ_VERSION' + | 'READ_DATE' + | 'SET_DATE' + | 'READ_TIME' + | 'SET_TIME'; + meter_phase_enum: 'SINGLE_PHASE' | 'THREE_PHASE'; + meter_type_enum: 'HPS' | 'FS'; + mhi_operation_enum: 'ADD' | 'REMOVE'; + mhi_status_enum: 'PENDING' | 'PROCESSING' | 'SUCCESSFUL' | 'FAILED'; + mppt_type_enum: 'MPPT' | 'PV_INVERTER'; + notification_status_enum: + | 'PENDING' + | 'PROCESSING' + | 'RECEIVED_BY_API' + | 'FAILED' + | 'SUCCESSFUL' + | 'READ' + | 'UNKNOWN'; + notification_type_enum: + | 'GRID_IS_HPS_ON_STATE_CHANGE' + | 'GRID_IS_FS_ON_STATE_CHANGE' + | 'GRID_METERING_HARDWARE_STATE_CHANGE' + | 'FS_RULE_EXECUTION_COMING_UP' + | 'FS_RULE_CHANGED' + | 'TARIFF_RULE_CHANGED' + | 'CLEAN_PANELS_REMINDER' + | 'GRID_REVENUE' + | 'PASSWORD_RESET' + | 'INVITE' + | 'AUTO_PAYOUT_GENRATION_REPORT' + | 'CREDIT_SENT' + | 'CREDIT_RECEIVED' + | 'METER_TOPPED_UP' + | 'PAYMENT_REJECTED' + | 'SITE_SUBMISSION'; + order_actor_type_enum: + | 'BANKING_SYSTEM' + | 'ORGANIZATION' + | 'CONNECTION' + | 'METER' + | 'AGENT' + | 'CUSTOMER'; + order_status_enum: + | 'INITIALISED' + | 'PENDING' + | 'COMPLETED' + | 'FAILED' + | 'CANCELLED' + | 'TIMED_OUT' + | 'IGNORED'; + order_type_enum: + | 'ENERGY_TOPUP' + | 'CONNECTION_PAYMENT' + | 'CONNECTION_REFUND' + | 'AGENT_WITHDRAWAL' + | 'AGENT_TOPUP' + | 'ORGANIZATION_TOPUP' + | 'ORGANIZATION_WITHDRAWAL' + | 'CUSTOMER_TOPUP'; + organization_type_enum: 'SOLAR_DEVELOPER' | 'LENDER' | 'DATA_AGGREGATOR'; + payment_channel_enum: 'USSD' | 'AYRTON' | 'NIFFLER' | 'TELEGRAM'; + payment_method_enum: 'CREDIT_CARD' | 'USSD' | 'BANK_TRANSFER'; + payout_status_enum: + | 'INITIALISED' + | 'WAITING_FOR_APPROVAL' + | 'PROCESSING' + | 'SUCCESSFUL' + | 'FAILED'; + pd_action_status_enum: + | 'GENERATING' + | 'GENERATION_FAILED' + | 'GENERATION_COMPLETED' + | 'ACTIONABLE' + | 'ACTION_COMPLETED'; + pd_action_type_enum: 'UPLOAD' | 'TEMPLATE' | 'EXTERNAL' | 'START' | 'END'; + pd_document_type_enum: 'GOOGLE_SHEETS' | 'GOOGLE_DOCS'; + solcast_cache_request_type_enum: 'ESTIMATED_ACTUALS' | 'FORECAST'; + transaction_status_enum: 'SUCCESSFUL' | 'FAILED'; + wallet_type_enum: 'VIRTUAL' | 'REAL'; + weather_type_enum: + | 'CLOUDY' + | 'CLOUDS' + | 'SHOWERS' + | 'SUNNY' + | 'UNKNOWN' + | 'CLOUDY_WITH_RAIN'; + }; + CompositeTypes: { + [_ in never]: never; + }; + }; +}; + +type DatabaseWithoutInternals = Omit; + +type DefaultSchema = DatabaseWithoutInternals[Extract< + keyof Database, + 'public' +>]; + +export type Tables< + DefaultSchemaTableNameOrOptions extends + | keyof (DefaultSchema['Tables'] & DefaultSchema['Views']) + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals; + } + ? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Views']) + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals; +} + ? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Views'])[TableName] extends { + Row: infer R; + } + ? R + : never + : DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema['Tables'] & + DefaultSchema['Views']) + ? (DefaultSchema['Tables'] & + DefaultSchema['Views'])[DefaultSchemaTableNameOrOptions] extends { + Row: infer R; + } + ? R + : never + : never; + +export type TablesInsert< + DefaultSchemaTableNameOrOptions extends + | keyof DefaultSchema['Tables'] + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals; + } + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'] + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals; +} + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'][TableName] extends { + Insert: infer I; + } + ? I + : never + : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema['Tables'] + ? DefaultSchema['Tables'][DefaultSchemaTableNameOrOptions] extends { + Insert: infer I; + } + ? I + : never + : never; + +export type TablesUpdate< + DefaultSchemaTableNameOrOptions extends + | keyof DefaultSchema['Tables'] + | { schema: keyof DatabaseWithoutInternals }, + TableName extends DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals; + } + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'] + : never = never, +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals; +} + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'][TableName] extends { + Update: infer U; + } + ? U + : never + : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema['Tables'] + ? DefaultSchema['Tables'][DefaultSchemaTableNameOrOptions] extends { + Update: infer U; + } + ? U + : never + : never; + +export type Enums< + DefaultSchemaEnumNameOrOptions extends + | keyof DefaultSchema['Enums'] + | { schema: keyof DatabaseWithoutInternals }, + EnumName extends DefaultSchemaEnumNameOrOptions extends { + schema: keyof DatabaseWithoutInternals; + } + ? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions['schema']]['Enums'] + : never = never, +> = DefaultSchemaEnumNameOrOptions extends { + schema: keyof DatabaseWithoutInternals; +} + ? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions['schema']]['Enums'][EnumName] + : DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema['Enums'] + ? DefaultSchema['Enums'][DefaultSchemaEnumNameOrOptions] + : never; + +export type CompositeTypes< + PublicCompositeTypeNameOrOptions extends + | keyof DefaultSchema['CompositeTypes'] + | { schema: keyof DatabaseWithoutInternals }, + CompositeTypeName extends PublicCompositeTypeNameOrOptions extends { + schema: keyof DatabaseWithoutInternals; + } + ? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions['schema']]['CompositeTypes'] + : never = never, +> = PublicCompositeTypeNameOrOptions extends { + schema: keyof DatabaseWithoutInternals; +} + ? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions['schema']]['CompositeTypes'][CompositeTypeName] + : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema['CompositeTypes'] + ? DefaultSchema['CompositeTypes'][PublicCompositeTypeNameOrOptions] + : never; + +export const Constants = { + public: { + Enums: { + account_type_enum: [ 'AGENT', 'MEMBER', 'CUSTOMER' ], + communication_protocol_enum: [ 'CALIN_V1', 'CALIN_V2', 'CALIN_LORAWAN' ], + currency_enum: [ 'USD', 'NGN', 'EUR' ], + directive_direction_enum: [ 'UP', 'DOWN' ], + directive_error_enum: [ + 'GRID_DOWN', + 'DCU_OFFLINE', + 'NO_METER', + 'NO_DCU', + 'NO_GRID', + 'NO_CONNECTION', + 'NO_CUSTOMER', + ], + directive_phase_enum: [ 'A', 'B', 'C' ], + directive_special_status_enum: [ + 'POWER_LIMIT_BREACHED', + 'CREDIT_EXHAUSTED', + 'REMOTE_SWITCHED_OFF', + 'OVER_VOLTAGE', + 'METER_NOT_ACTIVATED', + 'TAMPER', + 'LOW_VOLTAGE', + ], + directive_status_enum: [ + 'INITIALISED', + 'PENDING', + 'SENT_TO_API', + 'RECEIVED_BY_API', + 'SENT_TO_DCU', + 'RECEIVED_BY_DCU', + 'SENT_TO_METER', + 'RECEIVED_BY_METER', + 'SUCCESSFUL', + 'FAILED', + 'IGNORED', + 'CANCELLED', + 'TIMED_OUT', + 'UNKNOWN', + ], + directive_type_enum: [ + 'ON', + 'OFF', + 'READ_VOLTAGE', + 'PLS', + 'PLR', + 'CLEAR_TAMPER', + 'READ_METER_VERSION', + 'TOP_UP', + 'READ_CURRENT_CREDIT', + 'READ_CURRENT', + 'READ_POWER', + 'READ_SPECIAL_STATUS', + 'CLEAR_CREDIT', + 'READ_RELAY_STATUS', + 'READ_POWER_DOWN_COUNT', + 'READ_TIME', + 'READ_DATE', + 'READ_VOLTAGE_A', + 'READ_VOLTAGE_B', + 'READ_VOLTAGE_C', + 'READ_POWER_A', + 'READ_POWER_B', + 'READ_POWER_C', + 'READ_CURRENT_A', + 'READ_CURRENT_B', + 'READ_CURRENT_C', + 'READ_TOTAL_ACTIVE_KWH', + 'READ_STATUS', + 'SEND_TOKEN', + 'READ_FRAUD_STATUS', + 'READ_REPORT', + 'WRITE_DATE', + 'WRITE_TIME', + 'CLEAR_TAMPER_TOKEN', + 'READ_REPORT_UP', + 'READ_REPORT_DOWN', + 'READ_CREDIT_DOWN', + 'READ_CREDIT_UP', + 'READ_VOLTAGE_UP', + 'READ_VOLTAGE_DOWN', + 'READ_POWER_UP', + 'READ_POWER_DOWN', + 'READ_CURRENT_UP', + 'READ_CURRENT_DOWN', + 'CLEAR_TAMPER_UP', + 'CLEAR_TAMPER_DOWN', + 'TOP_UP_DOWN', + 'POWER_LIMIT_SET_UP', + 'POWER_LIMIT_SET_DOWN', + 'OPEN_RELAY_UP', + 'OPEN_RELAY_DOWN', + 'CLOSE_RELAY_UP', + 'CLOSE_RELAY_DOWN', + 'READ_VOLTAGE_A_UP', + 'READ_VOLTAGE_A_DOWN', + 'READ_POWER_A_UP', + 'READ_POWER_A_DOWN', + 'READ_CURRENT_A_UP', + 'READ_CURRENT_A_DOWN', + 'UNKNOWN', + 'CLEAR_CREDIT_DOWN', + 'TOP_UP_KWH', + 'TOKEN_ACCEPTED', + 'TOKEN_REJECTED', + 'ON_OFF_ACCEPTED', + 'ON_OFF_REJECTED', + ], + external_system_enum: [ + 'STEAMACO', + 'CALIN', + 'SOLCAST', + 'VICTRON', + 'FLUTTERWAVE', + 'AFRICASTALKING', + 'JOTFORM', + 'EPICOLLECT', + 'JIRA', + 'TELEGRAM', + 'ZEROTIER', + 'MAKE', + 'FLOW_XO', + 'SENDGRID', + 'ACREL', + ], + fs_command_type_enum: [ 'ON', 'OFF' ], + gender_enum: [ 'MALE', 'FEMALE' ], + generator_type_enum: [ 'SMALL', 'LARGE' ], + id_document_type_enum: [ + 'PASSPORT', + 'NATIONAL_ID', + 'DRIVING_LICENSE', + 'VOTERS_CARD', + ], + issue_status_enum: [ 'OPEN', 'CLOSED', 'OVERRIDDEN' ], + issue_type_enum: [ + 'NO_COMMUNICATION', + 'METER_NOT_ACTIVATED', + 'TAMPER', + 'POWER_LIMIT_BREACHED', + 'OVER_VOLTAGE', + 'LOW_VOLTAGE', + 'POWER_LIMIT_BAD_CONFIGURATION', + 'METER_STATE_BAD_CONFIGURATION', + 'UNEXPECTED_POWER_LIMIT', + 'UNEXPECTED_METER_STATUS', + 'NO_CREDIT', + 'NO_CONSUMPTION', + 'NUMBER_OF_PHASES', + 'VEBUS_STATE', + 'VEBUS_ERROR', + 'QUATTRO_TEMPERATURE_ALARM', + 'QUATTRO_OVERLOAD_ALARM', + 'HIGH_BATTERY_TEMPERATURE_ALARM', + 'CELL_IMBALANCE_ALARM', + 'HIGH_CHARGE_CURRENT_ALARM', + 'HIGH_CHARGE_TEMPERATURE_ALARM', + 'BATTERY_INTERNAL_FAILURE', + 'BATTERY_CHARGE_BLOCKED_ALARM', + 'BATTERY_DISCHARGE_BLOCKED_ALARM', + ], + member_type_enum: [ + 'SUPERADMIN', + 'ADMIN', + 'PARTNER', + 'FINANCE', + 'DEVELOPER', + 'MANAGER', + 'SUPPORT', + 'SERVICE', + 'SALES', + 'TECH', + ], + meter_commissioning_status_enum: [ + 'PENDING', + 'PROCESSING', + 'SUCCESSFUL', + 'FAILED', + ], + meter_credit_transfer_status_enum: [ + 'PENDING', + 'PROCESSING', + 'SUCCESSFUL', + 'FAILED', + ], + meter_interaction_status_enum: [ + 'QUEUED', + 'ABORTED', + 'PROCESSING', + 'SUCCESSFUL', + 'FAILED', + 'DEFERRED', + 'SUSPENDED', + ], + meter_interaction_type_enum: [ + 'READ_CREDIT', + 'READ_POWER_LIMIT', + 'READ_VOLTAGE', + 'SET_POWER_LIMIT', + 'TOP_UP', + 'TURN_ON', + 'TURN_OFF', + 'READ_POWER', + 'READ_CURRENT', + 'CLEAR_CREDIT', + 'CLEAR_TAMPER', + 'READ_REPORT', + 'JOIN_NETWORK', + 'DELIVER_PREEXISTING_TOKEN', + 'READ_VERSION', + 'READ_DATE', + 'SET_DATE', + 'READ_TIME', + 'SET_TIME', + ], + meter_phase_enum: [ 'SINGLE_PHASE', 'THREE_PHASE' ], + meter_type_enum: [ 'HPS', 'FS' ], + mhi_operation_enum: [ 'ADD', 'REMOVE' ], + mhi_status_enum: [ 'PENDING', 'PROCESSING', 'SUCCESSFUL', 'FAILED' ], + mppt_type_enum: [ 'MPPT', 'PV_INVERTER' ], + notification_status_enum: [ + 'PENDING', + 'PROCESSING', + 'RECEIVED_BY_API', + 'FAILED', + 'SUCCESSFUL', + 'READ', + 'UNKNOWN', + ], + notification_type_enum: [ + 'GRID_IS_HPS_ON_STATE_CHANGE', + 'GRID_IS_FS_ON_STATE_CHANGE', + 'GRID_METERING_HARDWARE_STATE_CHANGE', + 'FS_RULE_EXECUTION_COMING_UP', + 'FS_RULE_CHANGED', + 'TARIFF_RULE_CHANGED', + 'CLEAN_PANELS_REMINDER', + 'GRID_REVENUE', + 'PASSWORD_RESET', + 'INVITE', + 'AUTO_PAYOUT_GENRATION_REPORT', + 'CREDIT_SENT', + 'CREDIT_RECEIVED', + 'METER_TOPPED_UP', + 'PAYMENT_REJECTED', + 'SITE_SUBMISSION', + ], + order_actor_type_enum: [ + 'BANKING_SYSTEM', + 'ORGANIZATION', + 'CONNECTION', + 'METER', + 'AGENT', + 'CUSTOMER', + ], + order_status_enum: [ + 'INITIALISED', + 'PENDING', + 'COMPLETED', + 'FAILED', + 'CANCELLED', + 'TIMED_OUT', + 'IGNORED', + ], + order_type_enum: [ + 'ENERGY_TOPUP', + 'CONNECTION_PAYMENT', + 'CONNECTION_REFUND', + 'AGENT_WITHDRAWAL', + 'AGENT_TOPUP', + 'ORGANIZATION_TOPUP', + 'ORGANIZATION_WITHDRAWAL', + 'CUSTOMER_TOPUP', + ], + organization_type_enum: [ 'SOLAR_DEVELOPER', 'LENDER', 'DATA_AGGREGATOR' ], + payment_channel_enum: [ 'USSD', 'AYRTON', 'NIFFLER', 'TELEGRAM' ], + payment_method_enum: [ 'CREDIT_CARD', 'USSD', 'BANK_TRANSFER' ], + payout_status_enum: [ + 'INITIALISED', + 'WAITING_FOR_APPROVAL', + 'PROCESSING', + 'SUCCESSFUL', + 'FAILED', + ], + pd_action_status_enum: [ + 'GENERATING', + 'GENERATION_FAILED', + 'GENERATION_COMPLETED', + 'ACTIONABLE', + 'ACTION_COMPLETED', + ], + pd_action_type_enum: [ 'UPLOAD', 'TEMPLATE', 'EXTERNAL', 'START', 'END' ], + pd_document_type_enum: [ 'GOOGLE_SHEETS', 'GOOGLE_DOCS' ], + solcast_cache_request_type_enum: [ 'ESTIMATED_ACTUALS', 'FORECAST' ], + transaction_status_enum: [ 'SUCCESSFUL', 'FAILED' ], + wallet_type_enum: [ 'VIRTUAL', 'REAL' ], + weather_type_enum: [ + 'CLOUDY', + 'CLOUDS', + 'SHOWERS', + 'SUNNY', + 'UNKNOWN', + 'CLOUDY_WITH_RAIN', + ], + }, + }, +} as const; + +// Schema: __InternalSupabase +// Schema: public +// Enums +export type AccountTypeEnum = Database['public']['Enums']['account_type_enum']; + +export type CommunicationProtocolEnum = + Database['public']['Enums']['communication_protocol_enum']; + +export type CurrencyEnum = Database['public']['Enums']['currency_enum']; + +export type DirectiveDirectionEnum = + Database['public']['Enums']['directive_direction_enum']; + +export type DirectiveErrorEnum = + Database['public']['Enums']['directive_error_enum']; + +export type DirectivePhaseEnum = + Database['public']['Enums']['directive_phase_enum']; + +export type DirectiveSpecialStatusEnum = + Database['public']['Enums']['directive_special_status_enum']; + +export type DirectiveStatusEnum = + Database['public']['Enums']['directive_status_enum']; + +export type DirectiveTypeEnum = + Database['public']['Enums']['directive_type_enum']; + +export type ExternalSystemEnum = + Database['public']['Enums']['external_system_enum']; + +export type FCommandTypeEnum = + Database['public']['Enums']['fs_command_type_enum']; + +export type GenderEnum = Database['public']['Enums']['gender_enum']; + +export type GeneratorTypeEnum = + Database['public']['Enums']['generator_type_enum']; + +export type IdDocumentTypeEnum = + Database['public']['Enums']['id_document_type_enum']; + +export type IssueStatusEnum = Database['public']['Enums']['issue_status_enum']; + +export type IssueTypeEnum = Database['public']['Enums']['issue_type_enum']; + +export type MemberTypeEnum = Database['public']['Enums']['member_type_enum']; + +export type MeterCommissioningStatusEnum = + Database['public']['Enums']['meter_commissioning_status_enum']; + +export type MeterCreditTransferStatusEnum = + Database['public']['Enums']['meter_credit_transfer_status_enum']; + +export type MeterInteractionStatusEnum = + Database['public']['Enums']['meter_interaction_status_enum']; + +export type MeterInteractionTypeEnum = + Database['public']['Enums']['meter_interaction_type_enum']; + +export type MeterPhaseEnum = Database['public']['Enums']['meter_phase_enum']; + +export type MeterTypeEnum = Database['public']['Enums']['meter_type_enum']; + +export type MhiOperationEnum = + Database['public']['Enums']['mhi_operation_enum']; + +export type MhiStatusEnum = Database['public']['Enums']['mhi_status_enum']; + +export type MpptTypeEnum = Database['public']['Enums']['mppt_type_enum']; + +export type NotificationStatusEnum = + Database['public']['Enums']['notification_status_enum']; + +export type NotificationTypeEnum = + Database['public']['Enums']['notification_type_enum']; + +export type OrderActorTypeEnum = + Database['public']['Enums']['order_actor_type_enum']; + +export type OrderStatusEnum = Database['public']['Enums']['order_status_enum']; + +export type OrderTypeEnum = Database['public']['Enums']['order_type_enum']; + +export type OrganizationTypeEnum = + Database['public']['Enums']['organization_type_enum']; + +export type PaymentChannelEnum = + Database['public']['Enums']['payment_channel_enum']; + +export type PaymentMethodEnum = + Database['public']['Enums']['payment_method_enum']; + +export type PayoutStatusEnum = + Database['public']['Enums']['payout_status_enum']; + +export type PdActionStatusEnum = + Database['public']['Enums']['pd_action_status_enum']; + +export type PdActionTypeEnum = + Database['public']['Enums']['pd_action_type_enum']; + +export type PdDocumentTypeEnum = + Database['public']['Enums']['pd_document_type_enum']; + +export type SolcastCacheRequestTypeEnum = + Database['public']['Enums']['solcast_cache_request_type_enum']; + +export type TransactionStatusEnum = + Database['public']['Enums']['transaction_status_enum']; + +export type WalletTypeEnum = Database['public']['Enums']['wallet_type_enum']; + +export type WeatherTypeEnum = Database['public']['Enums']['weather_type_enum']; + +// Tables +export type Account = Database['public']['Tables']['accounts']['Row']; +export type InsertAccount = Database['public']['Tables']['accounts']['Insert']; +export type UpdateAccount = Database['public']['Tables']['accounts']['Update']; + +export type Agent = Database['public']['Tables']['agents']['Row']; +export type InsertAgent = Database['public']['Tables']['agents']['Insert']; +export type UpdateAgent = Database['public']['Tables']['agents']['Update']; + +export type ApiKey = Database['public']['Tables']['api_keys']['Row']; +export type InsertApiKey = Database['public']['Tables']['api_keys']['Insert']; +export type UpdateApiKey = Database['public']['Tables']['api_keys']['Update']; + +export type Audit = Database['public']['Tables']['audits']['Row']; +export type InsertAudit = Database['public']['Tables']['audits']['Insert']; +export type UpdateAudit = Database['public']['Tables']['audits']['Update']; + +export type AutopilotExecution = + Database['public']['Tables']['autopilot_executions']['Row']; +export type InsertAutopilotExecution = + Database['public']['Tables']['autopilot_executions']['Insert']; +export type UpdateAutopilotExecution = + Database['public']['Tables']['autopilot_executions']['Update']; + +export type BankAccount = Database['public']['Tables']['bank_accounts']['Row']; +export type InsertBankAccount = + Database['public']['Tables']['bank_accounts']['Insert']; +export type UpdateBankAccount = + Database['public']['Tables']['bank_accounts']['Update']; + +export type Bank = Database['public']['Tables']['banks']['Row']; +export type InsertBank = Database['public']['Tables']['banks']['Insert']; +export type UpdateBank = Database['public']['Tables']['banks']['Update']; + +export type ConnectionRequestedMeter = + Database['public']['Tables']['connection_requested_meters']['Row']; +export type InsertConnectionRequestedMeter = + Database['public']['Tables']['connection_requested_meters']['Insert']; +export type UpdateConnectionRequestedMeter = + Database['public']['Tables']['connection_requested_meters']['Update']; + +export type Connection = Database['public']['Tables']['connections']['Row']; +export type InsertConnection = + Database['public']['Tables']['connections']['Insert']; +export type UpdateConnection = + Database['public']['Tables']['connections']['Update']; + +export type Customer = Database['public']['Tables']['customers']['Row']; +export type InsertCustomer = + Database['public']['Tables']['customers']['Insert']; +export type UpdateCustomer = + Database['public']['Tables']['customers']['Update']; + +export type Dcus = Database['public']['Tables']['dcus']['Row']; +export type InsertDcus = Database['public']['Tables']['dcus']['Insert']; +export type UpdateDcus = Database['public']['Tables']['dcus']['Update']; + +export type DeviceLog = Database['public']['Tables']['device_logs']['Row']; +export type InsertDeviceLog = + Database['public']['Tables']['device_logs']['Insert']; +export type UpdateDeviceLog = + Database['public']['Tables']['device_logs']['Update']; + +export type DeviceType = Database['public']['Tables']['device_types']['Row']; +export type InsertDeviceType = + Database['public']['Tables']['device_types']['Insert']; +export type UpdateDeviceType = + Database['public']['Tables']['device_types']['Update']; + +export type Device = Database['public']['Tables']['devices']['Row']; +export type InsertDevice = Database['public']['Tables']['devices']['Insert']; +export type UpdateDevice = Database['public']['Tables']['devices']['Update']; + +export type DirectiveBatchExecution = + Database['public']['Tables']['directive_batch_executions']['Row']; +export type InsertDirectiveBatchExecution = + Database['public']['Tables']['directive_batch_executions']['Insert']; +export type UpdateDirectiveBatchExecution = + Database['public']['Tables']['directive_batch_executions']['Update']; + +export type DirectiveBatch = + Database['public']['Tables']['directive_batches']['Row']; +export type InsertDirectiveBatch = + Database['public']['Tables']['directive_batches']['Insert']; +export type UpdateDirectiveBatch = + Database['public']['Tables']['directive_batches']['Update']; + +export type DirectiveWatchdogSession = + Database['public']['Tables']['directive_watchdog_sessions']['Row']; +export type InsertDirectiveWatchdogSession = + Database['public']['Tables']['directive_watchdog_sessions']['Insert']; +export type UpdateDirectiveWatchdogSession = + Database['public']['Tables']['directive_watchdog_sessions']['Update']; + +export type Directive = Database['public']['Tables']['directives']['Row']; +export type InsertDirective = + Database['public']['Tables']['directives']['Insert']; +export type UpdateDirective = + Database['public']['Tables']['directives']['Update']; + +export type EnergyCabin = Database['public']['Tables']['energy_cabins']['Row']; +export type InsertEnergyCabin = + Database['public']['Tables']['energy_cabins']['Insert']; +export type UpdateEnergyCabin = + Database['public']['Tables']['energy_cabins']['Update']; + +export type Feature = Database['public']['Tables']['features']['Row']; +export type InsertFeature = Database['public']['Tables']['features']['Insert']; +export type UpdateFeature = Database['public']['Tables']['features']['Update']; + +export type Grid = Database['public']['Tables']['grids']['Row']; +export type InsertGrid = Database['public']['Tables']['grids']['Insert']; +export type UpdateGrid = Database['public']['Tables']['grids']['Update']; + +export type Issue = Database['public']['Tables']['issues']['Row']; +export type InsertIssue = Database['public']['Tables']['issues']['Insert']; +export type UpdateIssue = Database['public']['Tables']['issues']['Update']; + +export type LorawanDirective = + Database['public']['Tables']['lorawan_directives']['Row']; +export type InsertLorawanDirective = + Database['public']['Tables']['lorawan_directives']['Insert']; +export type UpdateLorawanDirective = + Database['public']['Tables']['lorawan_directives']['Update']; + +export type MemberFeature = + Database['public']['Tables']['member_feature']['Row']; +export type InsertMemberFeature = + Database['public']['Tables']['member_feature']['Insert']; +export type UpdateMemberFeature = + Database['public']['Tables']['member_feature']['Update']; + +export type Member = Database['public']['Tables']['members']['Row']; +export type InsertMember = Database['public']['Tables']['members']['Insert']; +export type UpdateMember = Database['public']['Tables']['members']['Update']; + +export type MeterCommissioning = + Database['public']['Tables']['meter_commissionings']['Row']; +export type InsertMeterCommissioning = + Database['public']['Tables']['meter_commissionings']['Insert']; +export type UpdateMeterCommissioning = + Database['public']['Tables']['meter_commissionings']['Update']; + +export type MeterCreditTransfer = + Database['public']['Tables']['meter_credit_transfers']['Row']; +export type InsertMeterCreditTransfer = + Database['public']['Tables']['meter_credit_transfers']['Insert']; +export type UpdateMeterCreditTransfer = + Database['public']['Tables']['meter_credit_transfers']['Update']; + +export type MeterInteraction = + Database['public']['Tables']['meter_interactions']['Row']; +export type InsertMeterInteraction = + Database['public']['Tables']['meter_interactions']['Insert']; +export type UpdateMeterInteraction = + Database['public']['Tables']['meter_interactions']['Update']; + +export type MeteringHardwareImport = + Database['public']['Tables']['metering_hardware_imports']['Row']; +export type InsertMeteringHardwareImport = + Database['public']['Tables']['metering_hardware_imports']['Insert']; +export type UpdateMeteringHardwareImport = + Database['public']['Tables']['metering_hardware_imports']['Update']; + +export type MeteringHardwareInstallSession = + Database['public']['Tables']['metering_hardware_install_sessions']['Row']; +export type InsertMeteringHardwareInstallSession = + Database['public']['Tables']['metering_hardware_install_sessions']['Insert']; +export type UpdateMeteringHardwareInstallSession = + Database['public']['Tables']['metering_hardware_install_sessions']['Update']; + +export type Meter = Database['public']['Tables']['meters']['Row']; +export type InsertMeter = Database['public']['Tables']['meters']['Insert']; +export type UpdateMeter = Database['public']['Tables']['meters']['Update']; + +export type Migration = Database['public']['Tables']['migrations']['Row']; +export type InsertMigration = + Database['public']['Tables']['migrations']['Insert']; +export type UpdateMigration = + Database['public']['Tables']['migrations']['Update']; + +export type Mppt = Database['public']['Tables']['mppts']['Row']; +export type InsertMppt = Database['public']['Tables']['mppts']['Insert']; +export type UpdateMppt = Database['public']['Tables']['mppts']['Update']; + +export type Note = Database['public']['Tables']['notes']['Row']; +export type InsertNote = Database['public']['Tables']['notes']['Insert']; +export type UpdateNote = Database['public']['Tables']['notes']['Update']; + +export type NotificationParameter = + Database['public']['Tables']['notification_parameters']['Row']; +export type InsertNotificationParameter = + Database['public']['Tables']['notification_parameters']['Insert']; +export type UpdateNotificationParameter = + Database['public']['Tables']['notification_parameters']['Update']; + +export type Notification = Database['public']['Tables']['notifications']['Row']; +export type InsertNotification = + Database['public']['Tables']['notifications']['Insert']; +export type UpdateNotification = + Database['public']['Tables']['notifications']['Update']; + +export type Order = Database['public']['Tables']['orders']['Row']; +export type InsertOrder = Database['public']['Tables']['orders']['Insert']; +export type UpdateOrder = Database['public']['Tables']['orders']['Update']; + +export type Organization = Database['public']['Tables']['organizations']['Row']; +export type InsertOrganization = + Database['public']['Tables']['organizations']['Insert']; +export type UpdateOrganization = + Database['public']['Tables']['organizations']['Update']; + +export type Payout = Database['public']['Tables']['payouts']['Row']; +export type InsertPayout = Database['public']['Tables']['payouts']['Insert']; +export type UpdatePayout = Database['public']['Tables']['payouts']['Update']; + +export type PdActionTemplate = + Database['public']['Tables']['pd_action_templates']['Row']; +export type InsertPdActionTemplate = + Database['public']['Tables']['pd_action_templates']['Insert']; +export type UpdatePdActionTemplate = + Database['public']['Tables']['pd_action_templates']['Update']; + +export type PdAction = Database['public']['Tables']['pd_actions']['Row']; +export type InsertPdAction = + Database['public']['Tables']['pd_actions']['Insert']; +export type UpdatePdAction = + Database['public']['Tables']['pd_actions']['Update']; + +export type PdAudit = Database['public']['Tables']['pd_audits']['Row']; +export type InsertPdAudit = Database['public']['Tables']['pd_audits']['Insert']; +export type UpdatePdAudit = Database['public']['Tables']['pd_audits']['Update']; + +export type PdDocumentTemplate = + Database['public']['Tables']['pd_document_templates']['Row']; +export type InsertPdDocumentTemplate = + Database['public']['Tables']['pd_document_templates']['Insert']; +export type UpdatePdDocumentTemplate = + Database['public']['Tables']['pd_document_templates']['Update']; + +export type PdDocument = Database['public']['Tables']['pd_documents']['Row']; +export type InsertPdDocument = + Database['public']['Tables']['pd_documents']['Insert']; +export type UpdatePdDocument = + Database['public']['Tables']['pd_documents']['Update']; + +export type PdFlowTemplate = + Database['public']['Tables']['pd_flow_templates']['Row']; +export type InsertPdFlowTemplate = + Database['public']['Tables']['pd_flow_templates']['Insert']; +export type UpdatePdFlowTemplate = + Database['public']['Tables']['pd_flow_templates']['Update']; + +export type PdFlow = Database['public']['Tables']['pd_flows']['Row']; +export type InsertPdFlow = Database['public']['Tables']['pd_flows']['Insert']; +export type UpdatePdFlow = Database['public']['Tables']['pd_flows']['Update']; + +export type PdSectionTemplate = + Database['public']['Tables']['pd_section_templates']['Row']; +export type InsertPdSectionTemplate = + Database['public']['Tables']['pd_section_templates']['Insert']; +export type UpdatePdSectionTemplate = + Database['public']['Tables']['pd_section_templates']['Update']; + +export type PdSection = Database['public']['Tables']['pd_sections']['Row']; +export type InsertPdSection = + Database['public']['Tables']['pd_sections']['Insert']; +export type UpdatePdSection = + Database['public']['Tables']['pd_sections']['Update']; + +export type PdSiteSubmission = + Database['public']['Tables']['pd_site_submissions']['Row']; +export type InsertPdSiteSubmission = + Database['public']['Tables']['pd_site_submissions']['Insert']; +export type UpdatePdSiteSubmission = + Database['public']['Tables']['pd_site_submissions']['Update']; + +export type PdSite = Database['public']['Tables']['pd_sites']['Row']; +export type InsertPdSite = Database['public']['Tables']['pd_sites']['Insert']; +export type UpdatePdSite = Database['public']['Tables']['pd_sites']['Update']; + +export type Pole = Database['public']['Tables']['poles']['Row']; +export type InsertPole = Database['public']['Tables']['poles']['Insert']; +export type UpdatePole = Database['public']['Tables']['poles']['Update']; + +export type Router = Database['public']['Tables']['routers']['Row']; +export type InsertRouter = Database['public']['Tables']['routers']['Insert']; +export type UpdateRouter = Database['public']['Tables']['routers']['Update']; + +export type SolcastCache = Database['public']['Tables']['solcast_cache']['Row']; +export type InsertSolcastCache = + Database['public']['Tables']['solcast_cache']['Insert']; +export type UpdateSolcastCache = + Database['public']['Tables']['solcast_cache']['Update']; + +export type Transaction = Database['public']['Tables']['transactions']['Row']; +export type InsertTransaction = + Database['public']['Tables']['transactions']['Insert']; +export type UpdateTransaction = + Database['public']['Tables']['transactions']['Update']; + +export type UssdSessionHop = + Database['public']['Tables']['ussd_session_hops']['Row']; +export type InsertUssdSessionHop = + Database['public']['Tables']['ussd_session_hops']['Insert']; +export type UpdateUssdSessionHop = + Database['public']['Tables']['ussd_session_hops']['Update']; + +export type UssdSession = Database['public']['Tables']['ussd_sessions']['Row']; +export type InsertUssdSession = + Database['public']['Tables']['ussd_sessions']['Insert']; +export type UpdateUssdSession = + Database['public']['Tables']['ussd_sessions']['Update']; + +export type Wallet = Database['public']['Tables']['wallets']['Row']; +export type InsertWallet = Database['public']['Tables']['wallets']['Insert']; +export type UpdateWallet = Database['public']['Tables']['wallets']['Update']; + +// Views +export type AgentWithAccount = + Database['public']['Views']['agents_with_account']['Row']; + +export type BatchCommand = Database['public']['Views']['batch_commands']['Row']; + +export type CustomerWithAccount = + Database['public']['Views']['customers_with_account']['Row']; + +export type MeterWithAccountAndStatus = + Database['public']['Views']['meters_with_account_and_statuses']['Row']; + +// Functions +export type ArgsFindEnergyTopupRevenue = + Database['public']['Functions']['find_energy_topup_revenue']['Args']; +export type ReturnTypeFindEnergyTopupRevenue = + Database['public']['Functions']['find_energy_topup_revenue']['Returns']; + +export type ArgsFindTopSpender = + Database['public']['Functions']['find_top_spenders']['Args']; +export type ReturnTypeFindTopSpender = + Database['public']['Functions']['find_top_spenders']['Returns']; + +export type ArgsGetGridStatus = + Database['public']['Functions']['get_grid_status']['Args']; +export type ReturnTypeGetGridStatus = + Database['public']['Functions']['get_grid_status']['Returns']; + +export type ArgsLockNextOrder = + Database['public']['Functions']['lock_next_order']['Args']; +export type ReturnTypeLockNextOrder = + Database['public']['Functions']['lock_next_order']['Returns']; + +export type ArgsLockNextOrderAndWallet = + Database['public']['Functions']['lock_next_order_and_wallets']['Args']; +export type ReturnTypeLockNextOrderAndWallet = + Database['public']['Functions']['lock_next_order_and_wallets']['Returns']; + +export type ArgsLockNextPdAction = + Database['public']['Functions']['lock_next_pd_action']['Args']; +export type ReturnTypeLockNextPdAction = + Database['public']['Functions']['lock_next_pd_action']['Returns']; + +export type ArgsRlCheckIfLender = + Database['public']['Functions']['rls_check_if_lender']['Args']; +export type ReturnTypeRlCheckIfLender = + Database['public']['Functions']['rls_check_if_lender']['Returns']; + +export type ArgsRlCheckIfNxtMember = + Database['public']['Functions']['rls_check_if_nxt_member']['Args']; +export type ReturnTypeRlCheckIfNxtMember = + Database['public']['Functions']['rls_check_if_nxt_member']['Returns']; + +export type ArgsRlGetMemberOrgId = + Database['public']['Functions']['rls_get_member_org_id']['Args']; +export type ReturnTypeRlGetMemberOrgId = + Database['public']['Functions']['rls_get_member_org_id']['Returns']; diff --git a/legacy/libs/core/tsconfig.json b/legacy/libs/core/tsconfig.json new file mode 100644 index 0000000..3fbfb6e --- /dev/null +++ b/legacy/libs/core/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "emitDecoratorMetadata": true, + "esModuleInterop": true, + "composite": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} \ No newline at end of file diff --git a/legacy/libs/core/tsconfig.lib.json b/legacy/libs/core/tsconfig.lib.json new file mode 100644 index 0000000..119777d --- /dev/null +++ b/legacy/libs/core/tsconfig.lib.json @@ -0,0 +1,27 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "declaration": true, + "types": [ + "node" + ], + "target": "es2021", + "composite": true, + "declarationMap": true + }, + "include": [ + "src/**/*.ts", + "migration/1690451156799-init.ts" + ], + "exclude": [ + "jest.config.ts", + "src/**/*.spec.ts", + "src/**/*.test.ts" + ], + "references": [ + { + "path": "../helpers/tsconfig.lib.json" + } + ] +} diff --git a/legacy/libs/core/tsconfig.spec.json b/legacy/libs/core/tsconfig.spec.json new file mode 100644 index 0000000..e767c47 --- /dev/null +++ b/legacy/libs/core/tsconfig.spec.json @@ -0,0 +1,17 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} \ No newline at end of file diff --git a/libs/helpers/.babelrc b/legacy/libs/helpers/.babelrc similarity index 100% rename from libs/helpers/.babelrc rename to legacy/libs/helpers/.babelrc diff --git a/libs/helpers/README.md b/legacy/libs/helpers/README.md similarity index 100% rename from libs/helpers/README.md rename to legacy/libs/helpers/README.md diff --git a/libs/helpers/jest.config.ts b/legacy/libs/helpers/jest.config.ts similarity index 100% rename from libs/helpers/jest.config.ts rename to legacy/libs/helpers/jest.config.ts diff --git a/libs/helpers/project.json b/legacy/libs/helpers/project.json similarity index 100% rename from libs/helpers/project.json rename to legacy/libs/helpers/project.json diff --git a/libs/helpers/src/array-helpers.spec.ts b/legacy/libs/helpers/src/array-helpers.spec.ts similarity index 100% rename from libs/helpers/src/array-helpers.spec.ts rename to legacy/libs/helpers/src/array-helpers.spec.ts diff --git a/libs/helpers/src/array-helpers.ts b/legacy/libs/helpers/src/array-helpers.ts similarity index 100% rename from libs/helpers/src/array-helpers.ts rename to legacy/libs/helpers/src/array-helpers.ts diff --git a/libs/helpers/src/computation-helpers.ts b/legacy/libs/helpers/src/computation-helpers.ts similarity index 100% rename from libs/helpers/src/computation-helpers.ts rename to legacy/libs/helpers/src/computation-helpers.ts diff --git a/libs/helpers/src/number-helpers.spec.ts b/legacy/libs/helpers/src/number-helpers.spec.ts similarity index 100% rename from libs/helpers/src/number-helpers.spec.ts rename to legacy/libs/helpers/src/number-helpers.spec.ts diff --git a/libs/helpers/src/number-helpers.ts b/legacy/libs/helpers/src/number-helpers.ts similarity index 100% rename from libs/helpers/src/number-helpers.ts rename to legacy/libs/helpers/src/number-helpers.ts diff --git a/libs/helpers/src/phone-helpers.ts b/legacy/libs/helpers/src/phone-helpers.ts similarity index 100% rename from libs/helpers/src/phone-helpers.ts rename to legacy/libs/helpers/src/phone-helpers.ts diff --git a/libs/helpers/src/pipes.ts b/legacy/libs/helpers/src/pipes.ts similarity index 100% rename from libs/helpers/src/pipes.ts rename to legacy/libs/helpers/src/pipes.ts diff --git a/libs/helpers/src/primitive-helpers.ts b/legacy/libs/helpers/src/primitive-helpers.ts similarity index 100% rename from libs/helpers/src/primitive-helpers.ts rename to legacy/libs/helpers/src/primitive-helpers.ts diff --git a/libs/helpers/src/promise-helpers.spec.ts b/legacy/libs/helpers/src/promise-helpers.spec.ts similarity index 100% rename from libs/helpers/src/promise-helpers.spec.ts rename to legacy/libs/helpers/src/promise-helpers.spec.ts diff --git a/libs/helpers/src/promise-helpers.ts b/legacy/libs/helpers/src/promise-helpers.ts similarity index 100% rename from libs/helpers/src/promise-helpers.ts rename to legacy/libs/helpers/src/promise-helpers.ts diff --git a/libs/helpers/src/query-helpers.ts b/legacy/libs/helpers/src/query-helpers.ts similarity index 100% rename from libs/helpers/src/query-helpers.ts rename to legacy/libs/helpers/src/query-helpers.ts diff --git a/libs/helpers/src/third-party-service-helpers.ts b/legacy/libs/helpers/src/third-party-service-helpers.ts similarity index 100% rename from libs/helpers/src/third-party-service-helpers.ts rename to legacy/libs/helpers/src/third-party-service-helpers.ts diff --git a/libs/helpers/src/time-helpers.ts b/legacy/libs/helpers/src/time-helpers.ts similarity index 100% rename from libs/helpers/src/time-helpers.ts rename to legacy/libs/helpers/src/time-helpers.ts diff --git a/libs/helpers/src/utilities.spec.ts b/legacy/libs/helpers/src/utilities.spec.ts similarity index 100% rename from libs/helpers/src/utilities.spec.ts rename to legacy/libs/helpers/src/utilities.spec.ts diff --git a/libs/helpers/src/utilities.ts b/legacy/libs/helpers/src/utilities.ts similarity index 100% rename from libs/helpers/src/utilities.ts rename to legacy/libs/helpers/src/utilities.ts diff --git a/libs/helpers/tsconfig.json b/legacy/libs/helpers/tsconfig.json similarity index 100% rename from libs/helpers/tsconfig.json rename to legacy/libs/helpers/tsconfig.json diff --git a/libs/helpers/tsconfig.lib.json b/legacy/libs/helpers/tsconfig.lib.json similarity index 100% rename from libs/helpers/tsconfig.lib.json rename to legacy/libs/helpers/tsconfig.lib.json diff --git a/libs/helpers/tsconfig.spec.json b/legacy/libs/helpers/tsconfig.spec.json similarity index 100% rename from libs/helpers/tsconfig.spec.json rename to legacy/libs/helpers/tsconfig.spec.json diff --git a/libs/timeseries/.env.example b/legacy/libs/timeseries/.env.example similarity index 100% rename from libs/timeseries/.env.example rename to legacy/libs/timeseries/.env.example diff --git a/libs/timeseries/README.md b/legacy/libs/timeseries/README.md similarity index 100% rename from libs/timeseries/README.md rename to legacy/libs/timeseries/README.md diff --git a/libs/timeseries/jest.config.ts b/legacy/libs/timeseries/jest.config.ts similarity index 100% rename from libs/timeseries/jest.config.ts rename to legacy/libs/timeseries/jest.config.ts diff --git a/libs/timeseries/migration/1683101854258-test.ts b/legacy/libs/timeseries/migration/1683101854258-test.ts similarity index 100% rename from libs/timeseries/migration/1683101854258-test.ts rename to legacy/libs/timeseries/migration/1683101854258-test.ts diff --git a/libs/timeseries/migration/1683102154639-test.ts b/legacy/libs/timeseries/migration/1683102154639-test.ts similarity index 100% rename from libs/timeseries/migration/1683102154639-test.ts rename to legacy/libs/timeseries/migration/1683102154639-test.ts diff --git a/libs/timeseries/migration/1683102268393-test.ts b/legacy/libs/timeseries/migration/1683102268393-test.ts similarity index 100% rename from libs/timeseries/migration/1683102268393-test.ts rename to legacy/libs/timeseries/migration/1683102268393-test.ts diff --git a/libs/timeseries/migration/1683105379191-test.ts b/legacy/libs/timeseries/migration/1683105379191-test.ts similarity index 100% rename from libs/timeseries/migration/1683105379191-test.ts rename to legacy/libs/timeseries/migration/1683105379191-test.ts diff --git a/libs/timeseries/migration/1683105415922-test.ts b/legacy/libs/timeseries/migration/1683105415922-test.ts similarity index 100% rename from libs/timeseries/migration/1683105415922-test.ts rename to legacy/libs/timeseries/migration/1683105415922-test.ts diff --git a/libs/timeseries/migration/1683105585641-test.ts b/legacy/libs/timeseries/migration/1683105585641-test.ts similarity index 100% rename from libs/timeseries/migration/1683105585641-test.ts rename to legacy/libs/timeseries/migration/1683105585641-test.ts diff --git a/libs/timeseries/migration/1683105969653-test.ts b/legacy/libs/timeseries/migration/1683105969653-test.ts similarity index 100% rename from libs/timeseries/migration/1683105969653-test.ts rename to legacy/libs/timeseries/migration/1683105969653-test.ts diff --git a/libs/timeseries/migration/1683106145280-test.ts b/legacy/libs/timeseries/migration/1683106145280-test.ts similarity index 100% rename from libs/timeseries/migration/1683106145280-test.ts rename to legacy/libs/timeseries/migration/1683106145280-test.ts diff --git a/libs/timeseries/migration/1683106171589-test.ts b/legacy/libs/timeseries/migration/1683106171589-test.ts similarity index 100% rename from libs/timeseries/migration/1683106171589-test.ts rename to legacy/libs/timeseries/migration/1683106171589-test.ts diff --git a/libs/timeseries/migration/1683106301541-test.ts b/legacy/libs/timeseries/migration/1683106301541-test.ts similarity index 100% rename from libs/timeseries/migration/1683106301541-test.ts rename to legacy/libs/timeseries/migration/1683106301541-test.ts diff --git a/libs/timeseries/migration/1683106552157-test.ts b/legacy/libs/timeseries/migration/1683106552157-test.ts similarity index 100% rename from libs/timeseries/migration/1683106552157-test.ts rename to legacy/libs/timeseries/migration/1683106552157-test.ts diff --git a/libs/timeseries/migration/1683106761507-test.ts b/legacy/libs/timeseries/migration/1683106761507-test.ts similarity index 100% rename from libs/timeseries/migration/1683106761507-test.ts rename to legacy/libs/timeseries/migration/1683106761507-test.ts diff --git a/libs/timeseries/migration/1683106867089-test.ts b/legacy/libs/timeseries/migration/1683106867089-test.ts similarity index 100% rename from libs/timeseries/migration/1683106867089-test.ts rename to legacy/libs/timeseries/migration/1683106867089-test.ts diff --git a/libs/timeseries/migration/1683106905132-test.ts b/legacy/libs/timeseries/migration/1683106905132-test.ts similarity index 100% rename from libs/timeseries/migration/1683106905132-test.ts rename to legacy/libs/timeseries/migration/1683106905132-test.ts diff --git a/libs/timeseries/migration/1684314621883-test.ts b/legacy/libs/timeseries/migration/1684314621883-test.ts similarity index 100% rename from libs/timeseries/migration/1684314621883-test.ts rename to legacy/libs/timeseries/migration/1684314621883-test.ts diff --git a/libs/timeseries/migration/1684315473174-test.ts b/legacy/libs/timeseries/migration/1684315473174-test.ts similarity index 100% rename from libs/timeseries/migration/1684315473174-test.ts rename to legacy/libs/timeseries/migration/1684315473174-test.ts diff --git a/libs/timeseries/migration/1686218151028-test.ts b/legacy/libs/timeseries/migration/1686218151028-test.ts similarity index 100% rename from libs/timeseries/migration/1686218151028-test.ts rename to legacy/libs/timeseries/migration/1686218151028-test.ts diff --git a/libs/timeseries/migration/1686218207546-test.ts b/legacy/libs/timeseries/migration/1686218207546-test.ts similarity index 100% rename from libs/timeseries/migration/1686218207546-test.ts rename to legacy/libs/timeseries/migration/1686218207546-test.ts diff --git a/libs/timeseries/migration/1687943049381-test.ts b/legacy/libs/timeseries/migration/1687943049381-test.ts similarity index 100% rename from libs/timeseries/migration/1687943049381-test.ts rename to legacy/libs/timeseries/migration/1687943049381-test.ts diff --git a/libs/timeseries/migration/1687943183742-test.ts b/legacy/libs/timeseries/migration/1687943183742-test.ts similarity index 100% rename from libs/timeseries/migration/1687943183742-test.ts rename to legacy/libs/timeseries/migration/1687943183742-test.ts diff --git a/libs/timeseries/migration/1687943234411-test.ts b/legacy/libs/timeseries/migration/1687943234411-test.ts similarity index 100% rename from libs/timeseries/migration/1687943234411-test.ts rename to legacy/libs/timeseries/migration/1687943234411-test.ts diff --git a/libs/timeseries/migration/1687943296316-test.ts b/legacy/libs/timeseries/migration/1687943296316-test.ts similarity index 100% rename from libs/timeseries/migration/1687943296316-test.ts rename to legacy/libs/timeseries/migration/1687943296316-test.ts diff --git a/libs/timeseries/migration/1687943330520-test.ts b/legacy/libs/timeseries/migration/1687943330520-test.ts similarity index 100% rename from libs/timeseries/migration/1687943330520-test.ts rename to legacy/libs/timeseries/migration/1687943330520-test.ts diff --git a/libs/timeseries/migration/1687943380756-test.ts b/legacy/libs/timeseries/migration/1687943380756-test.ts similarity index 100% rename from libs/timeseries/migration/1687943380756-test.ts rename to legacy/libs/timeseries/migration/1687943380756-test.ts diff --git a/libs/timeseries/migration/1687943411421-test.ts b/legacy/libs/timeseries/migration/1687943411421-test.ts similarity index 100% rename from libs/timeseries/migration/1687943411421-test.ts rename to legacy/libs/timeseries/migration/1687943411421-test.ts diff --git a/libs/timeseries/migration/1687943457023-test.ts b/legacy/libs/timeseries/migration/1687943457023-test.ts similarity index 100% rename from libs/timeseries/migration/1687943457023-test.ts rename to legacy/libs/timeseries/migration/1687943457023-test.ts diff --git a/libs/timeseries/migration/1688717812928-test.ts b/legacy/libs/timeseries/migration/1688717812928-test.ts similarity index 100% rename from libs/timeseries/migration/1688717812928-test.ts rename to legacy/libs/timeseries/migration/1688717812928-test.ts diff --git a/libs/timeseries/migration/1688718515778-test.ts b/legacy/libs/timeseries/migration/1688718515778-test.ts similarity index 100% rename from libs/timeseries/migration/1688718515778-test.ts rename to legacy/libs/timeseries/migration/1688718515778-test.ts diff --git a/libs/timeseries/migration/1688718675948-test.ts b/legacy/libs/timeseries/migration/1688718675948-test.ts similarity index 100% rename from libs/timeseries/migration/1688718675948-test.ts rename to legacy/libs/timeseries/migration/1688718675948-test.ts diff --git a/libs/timeseries/migration/1688718990786-test.ts b/legacy/libs/timeseries/migration/1688718990786-test.ts similarity index 100% rename from libs/timeseries/migration/1688718990786-test.ts rename to legacy/libs/timeseries/migration/1688718990786-test.ts diff --git a/libs/timeseries/migration/1688721020214-test.ts b/legacy/libs/timeseries/migration/1688721020214-test.ts similarity index 100% rename from libs/timeseries/migration/1688721020214-test.ts rename to legacy/libs/timeseries/migration/1688721020214-test.ts diff --git a/libs/timeseries/migration/1690193969548-test.ts b/legacy/libs/timeseries/migration/1690193969548-test.ts similarity index 100% rename from libs/timeseries/migration/1690193969548-test.ts rename to legacy/libs/timeseries/migration/1690193969548-test.ts diff --git a/libs/timeseries/migration/1690194267775-test.ts b/legacy/libs/timeseries/migration/1690194267775-test.ts similarity index 100% rename from libs/timeseries/migration/1690194267775-test.ts rename to legacy/libs/timeseries/migration/1690194267775-test.ts diff --git a/libs/timeseries/migration/1690295576111-test.ts b/legacy/libs/timeseries/migration/1690295576111-test.ts similarity index 100% rename from libs/timeseries/migration/1690295576111-test.ts rename to legacy/libs/timeseries/migration/1690295576111-test.ts diff --git a/libs/timeseries/migration/1690295601341-test.ts b/legacy/libs/timeseries/migration/1690295601341-test.ts similarity index 100% rename from libs/timeseries/migration/1690295601341-test.ts rename to legacy/libs/timeseries/migration/1690295601341-test.ts diff --git a/libs/timeseries/migration/1690528044268-test.ts b/legacy/libs/timeseries/migration/1690528044268-test.ts similarity index 100% rename from libs/timeseries/migration/1690528044268-test.ts rename to legacy/libs/timeseries/migration/1690528044268-test.ts diff --git a/libs/timeseries/migration/1693292222449-test.ts b/legacy/libs/timeseries/migration/1693292222449-test.ts similarity index 100% rename from libs/timeseries/migration/1693292222449-test.ts rename to legacy/libs/timeseries/migration/1693292222449-test.ts diff --git a/libs/timeseries/migration/1693292376380-test.ts b/legacy/libs/timeseries/migration/1693292376380-test.ts similarity index 100% rename from libs/timeseries/migration/1693292376380-test.ts rename to legacy/libs/timeseries/migration/1693292376380-test.ts diff --git a/libs/timeseries/migration/1693292405998-test.ts b/legacy/libs/timeseries/migration/1693292405998-test.ts similarity index 100% rename from libs/timeseries/migration/1693292405998-test.ts rename to legacy/libs/timeseries/migration/1693292405998-test.ts diff --git a/libs/timeseries/migration/1693292426837-test.ts b/legacy/libs/timeseries/migration/1693292426837-test.ts similarity index 100% rename from libs/timeseries/migration/1693292426837-test.ts rename to legacy/libs/timeseries/migration/1693292426837-test.ts diff --git a/libs/timeseries/migration/1693577132569-test.ts b/legacy/libs/timeseries/migration/1693577132569-test.ts similarity index 100% rename from libs/timeseries/migration/1693577132569-test.ts rename to legacy/libs/timeseries/migration/1693577132569-test.ts diff --git a/libs/timeseries/migration/1693577910622-test.ts b/legacy/libs/timeseries/migration/1693577910622-test.ts similarity index 100% rename from libs/timeseries/migration/1693577910622-test.ts rename to legacy/libs/timeseries/migration/1693577910622-test.ts diff --git a/libs/timeseries/migration/1693600253613-test.ts b/legacy/libs/timeseries/migration/1693600253613-test.ts similarity index 100% rename from libs/timeseries/migration/1693600253613-test.ts rename to legacy/libs/timeseries/migration/1693600253613-test.ts diff --git a/libs/timeseries/migration/1693600316329-test.ts b/legacy/libs/timeseries/migration/1693600316329-test.ts similarity index 100% rename from libs/timeseries/migration/1693600316329-test.ts rename to legacy/libs/timeseries/migration/1693600316329-test.ts diff --git a/libs/timeseries/migration/1693992268045-test.ts b/legacy/libs/timeseries/migration/1693992268045-test.ts similarity index 100% rename from libs/timeseries/migration/1693992268045-test.ts rename to legacy/libs/timeseries/migration/1693992268045-test.ts diff --git a/libs/timeseries/migration/1693995225452-test.ts b/legacy/libs/timeseries/migration/1693995225452-test.ts similarity index 100% rename from libs/timeseries/migration/1693995225452-test.ts rename to legacy/libs/timeseries/migration/1693995225452-test.ts diff --git a/libs/timeseries/migration/1694413819150-test.ts b/legacy/libs/timeseries/migration/1694413819150-test.ts similarity index 100% rename from libs/timeseries/migration/1694413819150-test.ts rename to legacy/libs/timeseries/migration/1694413819150-test.ts diff --git a/libs/timeseries/migration/1694413998176-test.ts b/legacy/libs/timeseries/migration/1694413998176-test.ts similarity index 100% rename from libs/timeseries/migration/1694413998176-test.ts rename to legacy/libs/timeseries/migration/1694413998176-test.ts diff --git a/libs/timeseries/migration/1694414407067-test.ts b/legacy/libs/timeseries/migration/1694414407067-test.ts similarity index 100% rename from libs/timeseries/migration/1694414407067-test.ts rename to legacy/libs/timeseries/migration/1694414407067-test.ts diff --git a/libs/timeseries/migration/1694696781281-test.ts b/legacy/libs/timeseries/migration/1694696781281-test.ts similarity index 100% rename from libs/timeseries/migration/1694696781281-test.ts rename to legacy/libs/timeseries/migration/1694696781281-test.ts diff --git a/libs/timeseries/migration/1694770502613-test.ts b/legacy/libs/timeseries/migration/1694770502613-test.ts similarity index 100% rename from libs/timeseries/migration/1694770502613-test.ts rename to legacy/libs/timeseries/migration/1694770502613-test.ts diff --git a/libs/timeseries/migration/1695640731512-test.ts b/legacy/libs/timeseries/migration/1695640731512-test.ts similarity index 100% rename from libs/timeseries/migration/1695640731512-test.ts rename to legacy/libs/timeseries/migration/1695640731512-test.ts diff --git a/libs/timeseries/migration/1696506503131-test.ts b/legacy/libs/timeseries/migration/1696506503131-test.ts similarity index 100% rename from libs/timeseries/migration/1696506503131-test.ts rename to legacy/libs/timeseries/migration/1696506503131-test.ts diff --git a/libs/timeseries/migration/1700487583187-rename_revenue_in_grid_business_snapshot_1_d.ts b/legacy/libs/timeseries/migration/1700487583187-rename_revenue_in_grid_business_snapshot_1_d.ts similarity index 100% rename from libs/timeseries/migration/1700487583187-rename_revenue_in_grid_business_snapshot_1_d.ts rename to legacy/libs/timeseries/migration/1700487583187-rename_revenue_in_grid_business_snapshot_1_d.ts diff --git a/libs/timeseries/migration/1701174507298-mppt_forecast_snapshot_1_h.ts b/legacy/libs/timeseries/migration/1701174507298-mppt_forecast_snapshot_1_h.ts similarity index 100% rename from libs/timeseries/migration/1701174507298-mppt_forecast_snapshot_1_h.ts rename to legacy/libs/timeseries/migration/1701174507298-mppt_forecast_snapshot_1_h.ts diff --git a/libs/timeseries/migration/1701174770898-mppt_forecast_snapshot_1_h_yphertable.ts b/legacy/libs/timeseries/migration/1701174770898-mppt_forecast_snapshot_1_h_yphertable.ts similarity index 100% rename from libs/timeseries/migration/1701174770898-mppt_forecast_snapshot_1_h_yphertable.ts rename to legacy/libs/timeseries/migration/1701174770898-mppt_forecast_snapshot_1_h_yphertable.ts diff --git a/libs/timeseries/migration/1701206158625-rename_columns_grid_energy_snapshot_15_min.ts b/legacy/libs/timeseries/migration/1701206158625-rename_columns_grid_energy_snapshot_15_min.ts similarity index 100% rename from libs/timeseries/migration/1701206158625-rename_columns_grid_energy_snapshot_15_min.ts rename to legacy/libs/timeseries/migration/1701206158625-rename_columns_grid_energy_snapshot_15_min.ts diff --git a/libs/timeseries/migration/1701298497981-rename_bugfix.ts b/legacy/libs/timeseries/migration/1701298497981-rename_bugfix.ts similarity index 100% rename from libs/timeseries/migration/1701298497981-rename_bugfix.ts rename to legacy/libs/timeseries/migration/1701298497981-rename_bugfix.ts diff --git a/libs/timeseries/migration/1702937728729-create_router_table.ts b/legacy/libs/timeseries/migration/1702937728729-create_router_table.ts similarity index 100% rename from libs/timeseries/migration/1702937728729-create_router_table.ts rename to legacy/libs/timeseries/migration/1702937728729-create_router_table.ts diff --git a/libs/timeseries/migration/1702937809805-create_router_table_hypertable.ts b/legacy/libs/timeseries/migration/1702937809805-create_router_table_hypertable.ts similarity index 100% rename from libs/timeseries/migration/1702937809805-create_router_table_hypertable.ts rename to legacy/libs/timeseries/migration/1702937809805-create_router_table_hypertable.ts diff --git a/libs/timeseries/migration/1702938239851-create_router_table_materialized_view_15_min.ts b/legacy/libs/timeseries/migration/1702938239851-create_router_table_materialized_view_15_min.ts similarity index 100% rename from libs/timeseries/migration/1702938239851-create_router_table_materialized_view_15_min.ts rename to legacy/libs/timeseries/migration/1702938239851-create_router_table_materialized_view_15_min.ts diff --git a/libs/timeseries/migration/1702938342590-create_router_table_agg_policy.ts b/legacy/libs/timeseries/migration/1702938342590-create_router_table_agg_policy.ts similarity index 100% rename from libs/timeseries/migration/1702938342590-create_router_table_agg_policy.ts rename to legacy/libs/timeseries/migration/1702938342590-create_router_table_agg_policy.ts diff --git a/libs/timeseries/migration/1703020648944-add_should_fs_be_on_to_grid_energy_table.ts b/legacy/libs/timeseries/migration/1703020648944-add_should_fs_be_on_to_grid_energy_table.ts similarity index 100% rename from libs/timeseries/migration/1703020648944-add_should_fs_be_on_to_grid_energy_table.ts rename to legacy/libs/timeseries/migration/1703020648944-add_should_fs_be_on_to_grid_energy_table.ts diff --git a/libs/timeseries/migration/1704203048454-add_connection_fee_tracking.ts b/legacy/libs/timeseries/migration/1704203048454-add_connection_fee_tracking.ts similarity index 100% rename from libs/timeseries/migration/1704203048454-add_connection_fee_tracking.ts rename to legacy/libs/timeseries/migration/1704203048454-add_connection_fee_tracking.ts diff --git a/libs/timeseries/migration/1709324026562-add_autopilot_debugger.ts b/legacy/libs/timeseries/migration/1709324026562-add_autopilot_debugger.ts similarity index 100% rename from libs/timeseries/migration/1709324026562-add_autopilot_debugger.ts rename to legacy/libs/timeseries/migration/1709324026562-add_autopilot_debugger.ts diff --git a/libs/timeseries/migration/1709724904951-add_iteration_id_to_autopilot.ts b/legacy/libs/timeseries/migration/1709724904951-add_iteration_id_to_autopilot.ts similarity index 100% rename from libs/timeseries/migration/1709724904951-add_iteration_id_to_autopilot.ts rename to legacy/libs/timeseries/migration/1709724904951-add_iteration_id_to_autopilot.ts diff --git a/libs/timeseries/migration/1710247121944-add_autopilot_fields.ts b/legacy/libs/timeseries/migration/1710247121944-add_autopilot_fields.ts similarity index 100% rename from libs/timeseries/migration/1710247121944-add_autopilot_fields.ts rename to legacy/libs/timeseries/migration/1710247121944-add_autopilot_fields.ts diff --git a/libs/timeseries/migration/1710248567378-change_battery_state.ts b/legacy/libs/timeseries/migration/1710248567378-change_battery_state.ts similarity index 100% rename from libs/timeseries/migration/1710248567378-change_battery_state.ts rename to legacy/libs/timeseries/migration/1710248567378-change_battery_state.ts diff --git a/libs/timeseries/migration/1713949762293-add_power_limit_to_meter_1h_snapshot.ts b/legacy/libs/timeseries/migration/1713949762293-add_power_limit_to_meter_1h_snapshot.ts similarity index 100% rename from libs/timeseries/migration/1713949762293-add_power_limit_to_meter_1h_snapshot.ts rename to legacy/libs/timeseries/migration/1713949762293-add_power_limit_to_meter_1h_snapshot.ts diff --git a/libs/timeseries/migration/1713949941429-add_power_limit_should_be_to_meter_1h_snapshot.ts b/legacy/libs/timeseries/migration/1713949941429-add_power_limit_should_be_to_meter_1h_snapshot.ts similarity index 100% rename from libs/timeseries/migration/1713949941429-add_power_limit_should_be_to_meter_1h_snapshot.ts rename to legacy/libs/timeseries/migration/1713949941429-add_power_limit_should_be_to_meter_1h_snapshot.ts diff --git a/libs/timeseries/migration/1713949941430-rename_power_limit_field.ts b/legacy/libs/timeseries/migration/1713949941430-rename_power_limit_field.ts similarity index 100% rename from libs/timeseries/migration/1713949941430-rename_power_limit_field.ts rename to legacy/libs/timeseries/migration/1713949941430-rename_power_limit_field.ts diff --git a/libs/timeseries/migration/1713949941431-rename_power_limit_should_be.ts b/legacy/libs/timeseries/migration/1713949941431-rename_power_limit_should_be.ts similarity index 100% rename from libs/timeseries/migration/1713949941431-rename_power_limit_should_be.ts rename to legacy/libs/timeseries/migration/1713949941431-rename_power_limit_should_be.ts diff --git a/libs/timeseries/migration/1714399697042-rename_power_limit_fields_again.ts b/legacy/libs/timeseries/migration/1714399697042-rename_power_limit_fields_again.ts similarity index 100% rename from libs/timeseries/migration/1714399697042-rename_power_limit_fields_again.ts rename to legacy/libs/timeseries/migration/1714399697042-rename_power_limit_fields_again.ts diff --git a/libs/timeseries/migration/1714400562554-rename_power_limit_fields_again.ts b/legacy/libs/timeseries/migration/1714400562554-rename_power_limit_fields_again.ts similarity index 100% rename from libs/timeseries/migration/1714400562554-rename_power_limit_fields_again.ts rename to legacy/libs/timeseries/migration/1714400562554-rename_power_limit_fields_again.ts diff --git a/libs/timeseries/migration/1714400791373-readd_power_limit_fields.ts b/legacy/libs/timeseries/migration/1714400791373-readd_power_limit_fields.ts similarity index 100% rename from libs/timeseries/migration/1714400791373-readd_power_limit_fields.ts rename to legacy/libs/timeseries/migration/1714400791373-readd_power_limit_fields.ts diff --git a/libs/timeseries/migration/1714461539866-add_fs_hps_classification.ts b/legacy/libs/timeseries/migration/1714461539866-add_fs_hps_classification.ts similarity index 100% rename from libs/timeseries/migration/1714461539866-add_fs_hps_classification.ts rename to legacy/libs/timeseries/migration/1714461539866-add_fs_hps_classification.ts diff --git a/libs/timeseries/migration/1714477272904-adding_is_on_fields_to_meter_consumption.ts b/legacy/libs/timeseries/migration/1714477272904-adding_is_on_fields_to_meter_consumption.ts similarity index 100% rename from libs/timeseries/migration/1714477272904-adding_is_on_fields_to_meter_consumption.ts rename to legacy/libs/timeseries/migration/1714477272904-adding_is_on_fields_to_meter_consumption.ts diff --git a/libs/timeseries/migration/1718272534998-add_extra_fields_to_mppt_asset_snapshots.ts b/legacy/libs/timeseries/migration/1718272534998-add_extra_fields_to_mppt_asset_snapshots.ts similarity index 100% rename from libs/timeseries/migration/1718272534998-add_extra_fields_to_mppt_asset_snapshots.ts rename to legacy/libs/timeseries/migration/1718272534998-add_extra_fields_to_mppt_asset_snapshots.ts diff --git a/libs/timeseries/migration/1718272656264-add_extra_fields_to_mppt_energy_snapshot_15_min.ts b/legacy/libs/timeseries/migration/1718272656264-add_extra_fields_to_mppt_energy_snapshot_15_min.ts similarity index 100% rename from libs/timeseries/migration/1718272656264-add_extra_fields_to_mppt_energy_snapshot_15_min.ts rename to legacy/libs/timeseries/migration/1718272656264-add_extra_fields_to_mppt_energy_snapshot_15_min.ts diff --git a/libs/timeseries/migration/1718272731122-add_extra_fields_to_mppt_energy_estimated_actual_snapshot_30_min.ts b/legacy/libs/timeseries/migration/1718272731122-add_extra_fields_to_mppt_energy_estimated_actual_snapshot_30_min.ts similarity index 100% rename from libs/timeseries/migration/1718272731122-add_extra_fields_to_mppt_energy_estimated_actual_snapshot_30_min.ts rename to legacy/libs/timeseries/migration/1718272731122-add_extra_fields_to_mppt_energy_estimated_actual_snapshot_30_min.ts diff --git a/libs/timeseries/migration/1718272745257-add_extra_fields_to_mppt_energy_forecast_1_h.ts b/legacy/libs/timeseries/migration/1718272745257-add_extra_fields_to_mppt_energy_forecast_1_h.ts similarity index 100% rename from libs/timeseries/migration/1718272745257-add_extra_fields_to_mppt_energy_forecast_1_h.ts rename to legacy/libs/timeseries/migration/1718272745257-add_extra_fields_to_mppt_energy_forecast_1_h.ts diff --git a/libs/timeseries/migration/1718272751345-add_extra_fields_to_mppt_energy_forecast_3_h.ts b/legacy/libs/timeseries/migration/1718272751345-add_extra_fields_to_mppt_energy_forecast_3_h.ts similarity index 100% rename from libs/timeseries/migration/1718272751345-add_extra_fields_to_mppt_energy_forecast_3_h.ts rename to legacy/libs/timeseries/migration/1718272751345-add_extra_fields_to_mppt_energy_forecast_3_h.ts diff --git a/libs/timeseries/migration/1718866357335-drop_loan_id.ts b/legacy/libs/timeseries/migration/1718866357335-drop_loan_id.ts similarity index 100% rename from libs/timeseries/migration/1718866357335-drop_loan_id.ts rename to legacy/libs/timeseries/migration/1718866357335-drop_loan_id.ts diff --git a/libs/timeseries/migration/1721980825122-add_ca_to_grid_energy_table.ts b/legacy/libs/timeseries/migration/1721980825122-add_ca_to_grid_energy_table.ts similarity index 100% rename from libs/timeseries/migration/1721980825122-add_ca_to_grid_energy_table.ts rename to legacy/libs/timeseries/migration/1721980825122-add_ca_to_grid_energy_table.ts diff --git a/libs/timeseries/migration/1722329972196-adding_new_issue_types.ts b/legacy/libs/timeseries/migration/1722329972196-adding_new_issue_types.ts similarity index 100% rename from libs/timeseries/migration/1722329972196-adding_new_issue_types.ts rename to legacy/libs/timeseries/migration/1722329972196-adding_new_issue_types.ts diff --git a/libs/timeseries/migration/1722349023421-add_connection_id_to_meter_snapshot_1_h.ts b/legacy/libs/timeseries/migration/1722349023421-add_connection_id_to_meter_snapshot_1_h.ts similarity index 100% rename from libs/timeseries/migration/1722349023421-add_connection_id_to_meter_snapshot_1_h.ts rename to legacy/libs/timeseries/migration/1722349023421-add_connection_id_to_meter_snapshot_1_h.ts diff --git a/libs/timeseries/migration/1722353749518-add_lifeline_connections_to_grid_business_snapshot.ts b/legacy/libs/timeseries/migration/1722353749518-add_lifeline_connections_to_grid_business_snapshot.ts similarity index 100% rename from libs/timeseries/migration/1722353749518-add_lifeline_connections_to_grid_business_snapshot.ts rename to legacy/libs/timeseries/migration/1722353749518-add_lifeline_connections_to_grid_business_snapshot.ts diff --git a/libs/timeseries/migration/1722587025788-rename_mppt_forecast_table.ts b/legacy/libs/timeseries/migration/1722587025788-rename_mppt_forecast_table.ts similarity index 100% rename from libs/timeseries/migration/1722587025788-rename_mppt_forecast_table.ts rename to legacy/libs/timeseries/migration/1722587025788-rename_mppt_forecast_table.ts diff --git a/libs/timeseries/migration/1722840973959-adding_three_phase_meter_count.ts b/legacy/libs/timeseries/migration/1722840973959-adding_three_phase_meter_count.ts similarity index 100% rename from libs/timeseries/migration/1722840973959-adding_three_phase_meter_count.ts rename to legacy/libs/timeseries/migration/1722840973959-adding_three_phase_meter_count.ts diff --git a/libs/timeseries/migration/1722863691413-adding_entities_to_postgres.ts b/legacy/libs/timeseries/migration/1722863691413-adding_entities_to_postgres.ts similarity index 100% rename from libs/timeseries/migration/1722863691413-adding_entities_to_postgres.ts rename to legacy/libs/timeseries/migration/1722863691413-adding_entities_to_postgres.ts diff --git a/libs/timeseries/migration/1722925537218-mppt_asset_snapshot_1_d.ts b/legacy/libs/timeseries/migration/1722925537218-mppt_asset_snapshot_1_d.ts similarity index 100% rename from libs/timeseries/migration/1722925537218-mppt_asset_snapshot_1_d.ts rename to legacy/libs/timeseries/migration/1722925537218-mppt_asset_snapshot_1_d.ts diff --git a/libs/timeseries/migration/1722937597910-drop_table.ts b/legacy/libs/timeseries/migration/1722937597910-drop_table.ts similarity index 100% rename from libs/timeseries/migration/1722937597910-drop_table.ts rename to legacy/libs/timeseries/migration/1722937597910-drop_table.ts diff --git a/libs/timeseries/migration/1722937716025-grid_forecast.ts b/legacy/libs/timeseries/migration/1722937716025-grid_forecast.ts similarity index 100% rename from libs/timeseries/migration/1722937716025-grid_forecast.ts rename to legacy/libs/timeseries/migration/1722937716025-grid_forecast.ts diff --git a/libs/timeseries/migration/1724241486790-refactoring_order_snapshots.ts b/legacy/libs/timeseries/migration/1724241486790-refactoring_order_snapshots.ts similarity index 100% rename from libs/timeseries/migration/1724241486790-refactoring_order_snapshots.ts rename to legacy/libs/timeseries/migration/1724241486790-refactoring_order_snapshots.ts diff --git a/libs/timeseries/migration/1725611187518-init.ts b/legacy/libs/timeseries/migration/1725611187518-init.ts similarity index 100% rename from libs/timeseries/migration/1725611187518-init.ts rename to legacy/libs/timeseries/migration/1725611187518-init.ts diff --git a/libs/timeseries/migration/1725611620912-init.ts b/legacy/libs/timeseries/migration/1725611620912-init.ts similarity index 100% rename from libs/timeseries/migration/1725611620912-init.ts rename to legacy/libs/timeseries/migration/1725611620912-init.ts diff --git a/libs/timeseries/migration/1733220594272-adding_orders_to_fdw.ts b/legacy/libs/timeseries/migration/1733220594272-adding_orders_to_fdw.ts similarity index 100% rename from libs/timeseries/migration/1733220594272-adding_orders_to_fdw.ts rename to legacy/libs/timeseries/migration/1733220594272-adding_orders_to_fdw.ts diff --git a/libs/timeseries/migration/1735653049007-add_hidden_and_cabin_info_to_meter_snapshot.ts b/legacy/libs/timeseries/migration/1735653049007-add_hidden_and_cabin_info_to_meter_snapshot.ts similarity index 100% rename from libs/timeseries/migration/1735653049007-add_hidden_and_cabin_info_to_meter_snapshot.ts rename to legacy/libs/timeseries/migration/1735653049007-add_hidden_and_cabin_info_to_meter_snapshot.ts diff --git a/libs/timeseries/migration/1736932065691-test.ts b/legacy/libs/timeseries/migration/1736932065691-test.ts similarity index 100% rename from libs/timeseries/migration/1736932065691-test.ts rename to legacy/libs/timeseries/migration/1736932065691-test.ts diff --git a/libs/timeseries/migration/1742465282563-test.ts b/legacy/libs/timeseries/migration/1742465282563-test.ts similarity index 100% rename from libs/timeseries/migration/1742465282563-test.ts rename to legacy/libs/timeseries/migration/1742465282563-test.ts diff --git a/libs/timeseries/migration/1742466202957-add_hypertable_to_data_sink.ts b/legacy/libs/timeseries/migration/1742466202957-add_hypertable_to_data_sink.ts similarity index 100% rename from libs/timeseries/migration/1742466202957-add_hypertable_to_data_sink.ts rename to legacy/libs/timeseries/migration/1742466202957-add_hypertable_to_data_sink.ts diff --git a/libs/timeseries/migration/1751893826308-adding_kwh_credit_to_meter_snapshots.ts b/legacy/libs/timeseries/migration/1751893826308-adding_kwh_credit_to_meter_snapshots.ts similarity index 100% rename from libs/timeseries/migration/1751893826308-adding_kwh_credit_to_meter_snapshots.ts rename to legacy/libs/timeseries/migration/1751893826308-adding_kwh_credit_to_meter_snapshots.ts diff --git a/libs/timeseries/migration/1751893826309-adding_kwh_credit_to_meter_snapshots copy.ts b/legacy/libs/timeseries/migration/1751893826309-adding_kwh_credit_to_meter_snapshots copy.ts similarity index 100% rename from libs/timeseries/migration/1751893826309-adding_kwh_credit_to_meter_snapshots copy.ts rename to legacy/libs/timeseries/migration/1751893826309-adding_kwh_credit_to_meter_snapshots copy.ts diff --git a/libs/timeseries/ormconfig.ts b/legacy/libs/timeseries/ormconfig.ts similarity index 100% rename from libs/timeseries/ormconfig.ts rename to legacy/libs/timeseries/ormconfig.ts diff --git a/libs/timeseries/project.json b/legacy/libs/timeseries/project.json similarity index 100% rename from libs/timeseries/project.json rename to legacy/libs/timeseries/project.json diff --git a/libs/timeseries/src/entities/dcu-snapshot-1-min.entity.ts b/legacy/libs/timeseries/src/entities/dcu-snapshot-1-min.entity.ts similarity index 100% rename from libs/timeseries/src/entities/dcu-snapshot-1-min.entity.ts rename to legacy/libs/timeseries/src/entities/dcu-snapshot-1-min.entity.ts diff --git a/libs/timeseries/src/entities/device-data-sink.entity.ts b/legacy/libs/timeseries/src/entities/device-data-sink.entity.ts similarity index 100% rename from libs/timeseries/src/entities/device-data-sink.entity.ts rename to legacy/libs/timeseries/src/entities/device-data-sink.entity.ts diff --git a/libs/timeseries/src/entities/exchange-rate-snapshot-1-d.entity.ts b/legacy/libs/timeseries/src/entities/exchange-rate-snapshot-1-d.entity.ts similarity index 100% rename from libs/timeseries/src/entities/exchange-rate-snapshot-1-d.entity.ts rename to legacy/libs/timeseries/src/entities/exchange-rate-snapshot-1-d.entity.ts diff --git a/libs/timeseries/src/entities/grid-business-snapshot-1-d.entity.ts b/legacy/libs/timeseries/src/entities/grid-business-snapshot-1-d.entity.ts similarity index 100% rename from libs/timeseries/src/entities/grid-business-snapshot-1-d.entity.ts rename to legacy/libs/timeseries/src/entities/grid-business-snapshot-1-d.entity.ts diff --git a/libs/timeseries/src/entities/grid-energy-snapshot-15-min.entity.ts b/legacy/libs/timeseries/src/entities/grid-energy-snapshot-15-min.entity.ts similarity index 100% rename from libs/timeseries/src/entities/grid-energy-snapshot-15-min.entity.ts rename to legacy/libs/timeseries/src/entities/grid-energy-snapshot-15-min.entity.ts diff --git a/libs/timeseries/src/entities/grid-forecast-snapshot-1-h.entity.ts b/legacy/libs/timeseries/src/entities/grid-forecast-snapshot-1-h.entity.ts similarity index 100% rename from libs/timeseries/src/entities/grid-forecast-snapshot-1-h.entity.ts rename to legacy/libs/timeseries/src/entities/grid-forecast-snapshot-1-h.entity.ts diff --git a/libs/timeseries/src/entities/meter-snapshot-1-h.entity.ts b/legacy/libs/timeseries/src/entities/meter-snapshot-1-h.entity.ts similarity index 100% rename from libs/timeseries/src/entities/meter-snapshot-1-h.entity.ts rename to legacy/libs/timeseries/src/entities/meter-snapshot-1-h.entity.ts diff --git a/libs/timeseries/src/entities/mppt-asset-snapshot-1-d.entity.ts b/legacy/libs/timeseries/src/entities/mppt-asset-snapshot-1-d.entity.ts similarity index 100% rename from libs/timeseries/src/entities/mppt-asset-snapshot-1-d.entity.ts rename to legacy/libs/timeseries/src/entities/mppt-asset-snapshot-1-d.entity.ts diff --git a/libs/timeseries/src/entities/mppt-energy-snapshot-15-min.entity.ts b/legacy/libs/timeseries/src/entities/mppt-energy-snapshot-15-min.entity.ts similarity index 100% rename from libs/timeseries/src/entities/mppt-energy-snapshot-15-min.entity.ts rename to legacy/libs/timeseries/src/entities/mppt-energy-snapshot-15-min.entity.ts diff --git a/libs/timeseries/src/entities/mppt-estimated-actual-snapshot-30-min.entity.ts b/legacy/libs/timeseries/src/entities/mppt-estimated-actual-snapshot-30-min.entity.ts similarity index 100% rename from libs/timeseries/src/entities/mppt-estimated-actual-snapshot-30-min.entity.ts rename to legacy/libs/timeseries/src/entities/mppt-estimated-actual-snapshot-30-min.entity.ts diff --git a/libs/timeseries/src/entities/mppt-forecast-30-min.entity.ts b/legacy/libs/timeseries/src/entities/mppt-forecast-30-min.entity.ts similarity index 100% rename from libs/timeseries/src/entities/mppt-forecast-30-min.entity.ts rename to legacy/libs/timeseries/src/entities/mppt-forecast-30-min.entity.ts diff --git a/libs/timeseries/src/entities/order-snapshot.entity.ts b/legacy/libs/timeseries/src/entities/order-snapshot.entity.ts similarity index 100% rename from libs/timeseries/src/entities/order-snapshot.entity.ts rename to legacy/libs/timeseries/src/entities/order-snapshot.entity.ts diff --git a/libs/timeseries/src/entities/organization-snapshot-1-d.entity.ts b/legacy/libs/timeseries/src/entities/organization-snapshot-1-d.entity.ts similarity index 100% rename from libs/timeseries/src/entities/organization-snapshot-1-d.entity.ts rename to legacy/libs/timeseries/src/entities/organization-snapshot-1-d.entity.ts diff --git a/libs/timeseries/src/entities/router-snapshot-1-min.entity.ts b/legacy/libs/timeseries/src/entities/router-snapshot-1-min.entity.ts similarity index 100% rename from libs/timeseries/src/entities/router-snapshot-1-min.entity.ts rename to legacy/libs/timeseries/src/entities/router-snapshot-1-min.entity.ts diff --git a/libs/timeseries/src/index.ts b/legacy/libs/timeseries/src/index.ts similarity index 100% rename from libs/timeseries/src/index.ts rename to legacy/libs/timeseries/src/index.ts diff --git a/libs/timeseries/src/modules/timeseries-typeorm.module.ts b/legacy/libs/timeseries/src/modules/timeseries-typeorm.module.ts similarity index 100% rename from libs/timeseries/src/modules/timeseries-typeorm.module.ts rename to legacy/libs/timeseries/src/modules/timeseries-typeorm.module.ts diff --git a/libs/timeseries/src/types/timeseries-entity.ts b/legacy/libs/timeseries/src/types/timeseries-entity.ts similarity index 100% rename from libs/timeseries/src/types/timeseries-entity.ts rename to legacy/libs/timeseries/src/types/timeseries-entity.ts diff --git a/libs/timeseries/tsconfig.json b/legacy/libs/timeseries/tsconfig.json similarity index 100% rename from libs/timeseries/tsconfig.json rename to legacy/libs/timeseries/tsconfig.json diff --git a/libs/timeseries/tsconfig.lib.json b/legacy/libs/timeseries/tsconfig.lib.json similarity index 100% rename from libs/timeseries/tsconfig.lib.json rename to legacy/libs/timeseries/tsconfig.lib.json diff --git a/libs/timeseries/tsconfig.spec.json b/legacy/libs/timeseries/tsconfig.spec.json similarity index 100% rename from libs/timeseries/tsconfig.spec.json rename to legacy/libs/timeseries/tsconfig.spec.json diff --git a/legacy/package.json b/legacy/package.json new file mode 100644 index 0000000..82e1431 --- /dev/null +++ b/legacy/package.json @@ -0,0 +1,127 @@ +{ + "name": "nxt-backend", + "version": "1.1.2", + "license": "MPL-2.0", + "authors": [ + { + "name": "Bobby Bol", + "email": "studio.innua@gmail.com" + }, + { + "name": "Tommaso Girotto", + "email": "tommaso.girotto91@gmail.com" + } + ], + "scripts": { + "check-types": "tsc -p tsconfig.base.json --noEmit --allowSyntheticDefaultImports", + "eslint": "eslint . --ext .ts", + "eslint:fix": "eslint . --ext .ts --fix", + "lint": "npm run eslint && npm run check-types", + "prepare": "husky", + "fix-node-cpu": "npm rebuild fsevents", + "build:tiamat": "nx build tiamat", + "preinstall": "npx npm-force-resolutions", + "build:loch": "nx build loch", + "build:yeti": "nx build yeti", + "gen-types-remote": "npx supabase@latest gen types typescript --project-id axenumkepgnwfmdogkqq > supabase/generated-types.ts", + "gen-types-local": "npx supabase@latest gen types typescript --local > supabase/generated-types.ts", + "gen-better-types": "npx better-supabase-types -i supabase/generated-types.ts -o libs/core/src/types/supabase-types.ts -s --enumAsType && node .scripts/fix-supabase-json-type.js && eslint --fix libs/core/src/types/supabase-types.ts && rm supabase/generated-types.ts", + "generate-types": "npm run gen-types-remote && npm run gen-better-types", + "generate-types:local": "npm run gen-types-local && npm run gen-better-types" + }, + "private": true, + "dependencies": { + "@aws-sdk/client-s3": "^3.842.0", + "@chirpstack/chirpstack-api": "^4.13.0", + "@grpc/grpc-js": "^1.13.4", + "@nestjs/axios": "4.0.0", + "@nestjs/common": "11.1.3", + "@nestjs/core": "11.1.3", + "@nestjs/jwt": "11.0.0", + "@nestjs/mapped-types": "^2.1.0", + "@nestjs/passport": "11.0.5", + "@nestjs/platform-express": "11.1.3", + "@nestjs/platform-socket.io": "11.1.3", + "@nestjs/schedule": "6.0.0", + "@nestjs/typeorm": "11.0.0", + "@nestjs/websockets": "11.1.3", + "@sendgrid/mail": "8.1.5", + "@supabase/supabase-js": "^2.103.3", + "africastalking": "^0.7.3", + "async-sema": "^3.1.1", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.2", + "flutterwave-node-v3": "1.3.0", + "form-data": "^4.0.3", + "iovalkey": "^0.3.3", + "lodash.throttle": "^4.1.1", + "moment": "^2.30.1", + "moment-timezone": "^0.6.0", + "mongodb": "^6.17.0", + "mqtt": "^5.13.2", + "passport-headerapikey": "^1.2.2", + "passport-http-bearer": "^1.0.1", + "pg": "^8.16.3", + "puppeteer": "^24.12.0", + "ramda": "^0.31.3", + "recast": "^0.23.11", + "reflect-metadata": "^0.2.2", + "rxjs": "^7.8.2", + "tslib": "^2.8.1", + "typeorm": "^0.3.25", + "typeorm-extension": "^3.7.1", + "ulid": "^3.0.1", + "uuid": "^11.1.0", + "winston": "^3.17.0", + "winston-loki": "^6.1.3" + }, + "devDependencies": { + "@eslint/js": "^9.30.1", + "@nestjs/schematics": "11.0.5", + "@nestjs/testing": "11.1.3", + "@nx/eslint": "21.2.2", + "@nx/eslint-plugin": "21.2.2", + "@nx/jest": "21.2.2", + "@nx/js": "21.2.2", + "@nx/nest": "21.2.2", + "@nx/node": "21.2.2", + "@nx/webpack": "21.2.2", + "@nx/workspace": "21.2.2", + "@types/express": "^5.0.3", + "@types/jest": "^30.0.0", + "@types/multer": "^2.0.0", + "@types/node": "~24.0.10", + "@types/passport-http-bearer": "^1.0.41", + "@types/pg": "^8.16.0", + "@types/ramda": "^0.31.1", + "@typescript-eslint/eslint-plugin": "8.36.0", + "@typescript-eslint/parser": "8.36.0", + "dotenv-safe": "^9.1.0", + "eslint": "^9.30.1", + "eslint-plugin-deno": "^0.0.1", + "eslint-plugin-eslint-comments": "^3.2.0", + "husky": "^9.1.7", + "jest": "^30.0.4", + "jest-environment-node": "^30.0.4", + "lint-staged": "^16.1.2", + "npm-force-resolutions": "^0.0.10", + "nx": "21.2.2", + "supabase": "^2.54.10", + "ts-jest": "29.4.0", + "ts-node": "10.9.2", + "typescript": "^5.8.3", + "typescript-eslint": "^8.36.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.3" + }, + "engines": { + "node": "22.x" + }, + "resolutions": { + "**/semver": "^7.7.2", + "**/word-wrap": "^1.2.4", + "**/cookie": "^0.7.0", + "**/validate.js": "^0.13.1" + } +} diff --git a/supabase/.env.example b/legacy/supabase/.env.example similarity index 100% rename from supabase/.env.example rename to legacy/supabase/.env.example diff --git a/legacy/supabase/config.toml b/legacy/supabase/config.toml new file mode 100644 index 0000000..a773855 --- /dev/null +++ b/legacy/supabase/config.toml @@ -0,0 +1,189 @@ +# A string used to distinguish different Supabase projects on the same host. Defaults to the +# working directory name when running `supabase init`. +project_id = "skyfox-legacy" + +[api] +enabled = true +# Port to use for the API URL. +port = 54321 +# Schemas to expose in your API. Tables, views and stored procedures in this schema will get API +# endpoints. `public` is always included. +schemas = ["public", "graphql_public"] +# Extra schemas to add to the search_path of every request. `public` is always included. +extra_search_path = ["public", "extensions"] +# The maximum number of rows returns from a view, table, or stored procedure. Limits payload size +# for accidental or malicious requests. +max_rows = 1000 + +[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 + +[realtime] +enabled = true +# Bind realtime via either IPv4 or IPv6. (default: IPv4) +# ip_version = "IPv6" +# The maximum length in bytes of HTTP request headers. (default: 4096) +# max_header_length = 4096 + +[studio] +enabled = true +# Port to use for Supabase Studio. +port = 54323 +# External URL of the API server that frontend connects to. +api_url = "http://127.0.0.1" +# OpenAI API Key to use for Supabase AI in the Supabase Studio. +openai_api_key = "env(OPENAI_API_KEY)" + +# Email testing server. Emails sent with the local dev setup are not actually sent - rather, they +# are monitored, and you can view the emails that would have been sent from the web interface. +[inbucket] +enabled = true +# Port to use for the email testing server web interface. +port = 54324 +# Uncomment to expose additional ports for testing user applications that send emails. +# smtp_port = 54325 +# pop3_port = 54326 + +[storage] +enabled = true +# The maximum file size allowed (e.g. "5MB", "500KB"). +file_size_limit = "50MiB" + +[storage.image_transformation] +enabled = true + +[auth] +enabled = true +# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used +# in emails. +site_url = "http://127.0.0.1:3000" +# A list of *exact* URLs that auth providers are permitted to redirect to post authentication. +additional_redirect_urls = ["https://127.0.0.1:3000"] +# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 (1 week). +jwt_expiry = 3600 +# If disabled, the refresh token will never expire. +enable_refresh_token_rotation = true +# Allows refresh tokens to be reused after expiry, up to the specified interval in seconds. +# Requires enable_refresh_token_rotation = true. +refresh_token_reuse_interval = 10 +# Allow/disallow new user signups to your project. +enable_signup = true +# Allow/disallow anonymous sign-ins to your project. +enable_anonymous_sign_ins = false +# Allow/disallow testing manual linking of accounts +enable_manual_linking = false + +[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 +# Controls the minimum amount of time that must pass before sending another signup confirmation or password reset email. +max_frequency = "1s" + +# 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 = true +# 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" + +# 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)" + +# 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.google] +enabled = false +client_id = "env(SUPABASE_AUTH_EXTERNAL_GOOGLE_CLIENT_ID)" +# DO NOT commit your OAuth provider secret to git. Use environment variable substitution instead: +secret = "env(SUPABASE_AUTH_EXTERNAL_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 = "" +# If enabled, the nonce check will be skipped. Required for local sign in with Google auth. +skip_nonce_check = false + +[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" +inspector_port = 8083 + +[analytics] +enabled = false +port = 54327 +vector_port = 54328 +# 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)" + +[functions.meter-consumption-2] +enabled = false +verify_jwt = true +import_map = "./functions/meter-consumption-2/deno.json" +# Uncomment to specify a custom file path to the entrypoint. +# Supported file extensions are: .ts, .js, .mjs, .jsx, .tsx +entrypoint = "./functions/meter-consumption-2/index.ts" +# Specifies static files to be bundled with the function. Supports glob patterns. +# For example, if you want to serve static HTML pages in your function: +# static_files = [ "./functions/meter-consumption-2/*.html" ] diff --git a/supabase/migrations/20251028091645_remote_schema.sql b/legacy/supabase/migrations/20251028091645_remote_schema.sql similarity index 100% rename from supabase/migrations/20251028091645_remote_schema.sql rename to legacy/supabase/migrations/20251028091645_remote_schema.sql diff --git a/supabase/migrations/20251028091646_dropped-positions-from-mppt.sql b/legacy/supabase/migrations/20251028091646_dropped-positions-from-mppt.sql similarity index 100% rename from supabase/migrations/20251028091646_dropped-positions-from-mppt.sql rename to legacy/supabase/migrations/20251028091646_dropped-positions-from-mppt.sql diff --git a/supabase/migrations/20251028091647_dropped-lat-lon-from-grids.sql b/legacy/supabase/migrations/20251028091647_dropped-lat-lon-from-grids.sql similarity index 100% rename from supabase/migrations/20251028091647_dropped-lat-lon-from-grids.sql rename to legacy/supabase/migrations/20251028091647_dropped-lat-lon-from-grids.sql diff --git a/supabase/migrations/20251103143749_removed_successful_perc_directive_batch.sql b/legacy/supabase/migrations/20251103143749_removed_successful_perc_directive_batch.sql similarity index 100% rename from supabase/migrations/20251103143749_removed_successful_perc_directive_batch.sql rename to legacy/supabase/migrations/20251103143749_removed_successful_perc_directive_batch.sql diff --git a/supabase/migrations/20251103150206_dropped-is-deleted-from-mppts.sql b/legacy/supabase/migrations/20251103150206_dropped-is-deleted-from-mppts.sql similarity index 100% rename from supabase/migrations/20251103150206_dropped-is-deleted-from-mppts.sql rename to legacy/supabase/migrations/20251103150206_dropped-is-deleted-from-mppts.sql diff --git a/supabase/migrations/20251105114534_remote_schema.sql b/legacy/supabase/migrations/20251105114534_remote_schema.sql similarity index 100% rename from supabase/migrations/20251105114534_remote_schema.sql rename to legacy/supabase/migrations/20251105114534_remote_schema.sql diff --git a/supabase/migrations/20251110171201_remote_schema.sql b/legacy/supabase/migrations/20251110171201_remote_schema.sql similarity index 100% rename from supabase/migrations/20251110171201_remote_schema.sql rename to legacy/supabase/migrations/20251110171201_remote_schema.sql diff --git a/supabase/migrations/20251111125610_remote_schema.sql b/legacy/supabase/migrations/20251111125610_remote_schema.sql similarity index 100% rename from supabase/migrations/20251111125610_remote_schema.sql rename to legacy/supabase/migrations/20251111125610_remote_schema.sql diff --git a/supabase/migrations/20251112170846_remote_schema.sql b/legacy/supabase/migrations/20251112170846_remote_schema.sql similarity index 100% rename from supabase/migrations/20251112170846_remote_schema.sql rename to legacy/supabase/migrations/20251112170846_remote_schema.sql diff --git a/supabase/migrations/20251202080034_remote_schema.sql b/legacy/supabase/migrations/20251202080034_remote_schema.sql similarity index 100% rename from supabase/migrations/20251202080034_remote_schema.sql rename to legacy/supabase/migrations/20251202080034_remote_schema.sql diff --git a/supabase/migrations/20251204142342_remote_schema.sql b/legacy/supabase/migrations/20251204142342_remote_schema.sql similarity index 100% rename from supabase/migrations/20251204142342_remote_schema.sql rename to legacy/supabase/migrations/20251204142342_remote_schema.sql diff --git a/supabase/migrations/20251210113224_remote_schema.sql b/legacy/supabase/migrations/20251210113224_remote_schema.sql similarity index 100% rename from supabase/migrations/20251210113224_remote_schema.sql rename to legacy/supabase/migrations/20251210113224_remote_schema.sql diff --git a/supabase/migrations/20251231155410_remote_schema.sql b/legacy/supabase/migrations/20251231155410_remote_schema.sql similarity index 100% rename from supabase/migrations/20251231155410_remote_schema.sql rename to legacy/supabase/migrations/20251231155410_remote_schema.sql diff --git a/supabase/migrations/20260211074929_remote_schema.sql b/legacy/supabase/migrations/20260211074929_remote_schema.sql similarity index 100% rename from supabase/migrations/20260211074929_remote_schema.sql rename to legacy/supabase/migrations/20260211074929_remote_schema.sql diff --git a/supabase/migrations/20260213091058_remote_schema.sql b/legacy/supabase/migrations/20260213091058_remote_schema.sql similarity index 100% rename from supabase/migrations/20260213091058_remote_schema.sql rename to legacy/supabase/migrations/20260213091058_remote_schema.sql diff --git a/supabase/migrations/20260226124907_remote_schema.sql b/legacy/supabase/migrations/20260226124907_remote_schema.sql similarity index 100% rename from supabase/migrations/20260226124907_remote_schema.sql rename to legacy/supabase/migrations/20260226124907_remote_schema.sql diff --git a/supabase/migrations/20260317145503_remote_schema.sql b/legacy/supabase/migrations/20260317145503_remote_schema.sql similarity index 100% rename from supabase/migrations/20260317145503_remote_schema.sql rename to legacy/supabase/migrations/20260317145503_remote_schema.sql diff --git a/supabase/migrations/20260318114439_remote_schema.sql b/legacy/supabase/migrations/20260318114439_remote_schema.sql similarity index 100% rename from supabase/migrations/20260318114439_remote_schema.sql rename to legacy/supabase/migrations/20260318114439_remote_schema.sql diff --git a/supabase/migrations/20260428131001_remote_schema.sql b/legacy/supabase/migrations/20260428131001_remote_schema.sql similarity index 100% rename from supabase/migrations/20260428131001_remote_schema.sql rename to legacy/supabase/migrations/20260428131001_remote_schema.sql diff --git a/legacy/tsconfig.base.json b/legacy/tsconfig.base.json new file mode 100644 index 0000000..b71eba6 --- /dev/null +++ b/legacy/tsconfig.base.json @@ -0,0 +1,77 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "rootDir": ".", + "sourceMap": true, + "declaration": false, + "moduleResolution": "node", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "importHelpers": true, + "target": "es2015", + "module": "esnext", + "lib": [ + "es2024", + "dom", + "dom.iterable" + ], + "types": [ + "multer", + "express", + "node", + "jest" + ], + "skipLibCheck": true, + "skipDefaultLibCheck": true, + "baseUrl": ".", + "paths": { + "@core": [ + "libs/core/src" + ], + "@core/*": [ + "libs/core/src/*" + ], + "@helpers": [ + "libs/helpers/src" + ], + "@helpers/*": [ + "libs/helpers/src/*" + ], + "@tiamat": [ + "apps/tiamat/src" + ], + "@tiamat/*": [ + "apps/tiamat/src/*" + ], + "@yeti": [ + "apps/yeti/src" + ], + "@yeti/*": [ + "apps/yeti/src/*" + ], + "@loch": [ + "apps/loch/src" + ], + "@loch/*": [ + "apps/loch/src/*" + ], + "@talos": [ + "apps/talos/src" + ], + "@talos/*": [ + "apps/talos/src/*" + ], + "@timeseries": [ + "libs/timeseries/src" + ], + "@timeseries/*": [ + "libs/timeseries/src/*" + ] + } + }, + "exclude": [ + "node_modules", + "tmp", + "supabase" + ] +} diff --git a/libs/core/.spec.swcrc b/libs/core/.spec.swcrc new file mode 100644 index 0000000..3b52a53 --- /dev/null +++ b/libs/core/.spec.swcrc @@ -0,0 +1,22 @@ +{ + "jsc": { + "target": "es2017", + "parser": { + "syntax": "typescript", + "decorators": true, + "dynamicImport": true + }, + "transform": { + "decoratorMetadata": true, + "legacyDecorator": true + }, + "keepClassNames": true, + "externalHelpers": true, + "loose": true + }, + "module": { + "type": "es6" + }, + "sourceMaps": true, + "exclude": [] +} diff --git a/libs/core/README.md b/libs/core/README.md index 36a7a9c..487ac8c 100644 --- a/libs/core/README.md +++ b/libs/core/README.md @@ -1,101 +1,34 @@ -# @core +# core -Shared NestJS library used by all apps in the monorepo. Provides database connections, shared domain entities and services, third-party API clients, and cross-cutting infrastructure modules. +Shared kernel (`@nxt/core`): config, generated Supabase types, cross-cutting infra. -**Path alias:** `@core` → `libs/core/src` +## Import style ---- +| Scope | Specifier | Notes | +|---|---|---| +| Bootstrap / config | `@nxt/core/config` | `loadConfig` / `getConfig` / `setConfig` / `requireEnv` — **no Nest modules** | +| Nest infra & helpers | `@nxt/core` | Logger, Global*Module, constants — not config | +| Generated / adjusted types | `@nxt/core/types/…` | Public type subpaths | +| Inside this package (cross-folder) | `#config/…`, `#modules/…`, `#types/…` | Package `"imports"` — direct files | +| Same folder / co-located sibling | relative `./…` | Prefer for local peers | -## What it provides +Do **not** import config from the fat `@nxt/core` barrel (it is not re-exported there). -- **Database connections** — TypeORM connection to the primary PostgreSQL database (`CoreTypeOrmModule`), raw `pg` pool (`CorePgModule`), and Supabase client (`GlobalSupabaseModule`) -- **Global infrastructure** — HTTP module (`GlobalHttpModule`), structured logging to Loki (`CoreLoggerModule`) -- **Shared domain layer** — TypeORM entities, DTOs, and services for all core mini-grid domain models -- **Third-party clients** — CALIN meter APIs, Victron VRM API, Make.com, Solcast, SendGrid +Example (in-package): `import { requireEnv } from '#config/require-env.js'`. ---- +## Supabase types -## Exported modules (`libs/core/src/index.ts`) +| Import | From | +|---|---| +| `Database` (client typing) | `@nxt/core/types/supabase-types-adjusted` | +| Enums, `Json`, table row/insert/update aliases | `@nxt/core/types/supabase-types` | -| Export | Description | -|--------|-------------| -| `CoreTypeOrmModule` | `TypeOrmModule.forRoot` for the primary PostgreSQL database. Registers all core entities. | -| `GlobalHttpModule` | Global `HttpModule` powered by `@nestjs/axios`. | -| `GlobalSupabaseModule` | Provides `SupabaseService` with a typed Supabase client and `throwSupabaseError` helper. | -| `CoreLoggerModule` | Global `LokiService` — Winston logger with Loki transport and console fallback. | -| `CorePgModule` | Global `PgService` — raw `pg` connection pool with `query` and `getClient` helpers. | +Regenerate generated types: `pnpm generate-types:local` (requires `pnpm supabase start`). -Apps also import deeply from `@core/modules/...` for domain services and entities. +## Building ---- +Run `nx build core` to build the library. -## Shared types +## Running unit tests -| File | Description | -|------|-------------| -| `types/core-entity.ts` | `CoreEntity` base class with `id` and `created_at` columns for main DB entities. | -| `types/supabase-types.ts` | Auto-generated and adjusted TypeScript types for the Supabase database schema. | -| `types/device-messaging.ts` | Shared enums for device messaging (e.g. `PhaseEnum`). | -| `types/solcast-type.ts` | Solcast API response types. | - ---- - -## Shared constants - -`libs/core/src/constants.ts` — shared application-wide constants such as `SUPABASE_QUERY_LIMIT` and wallet/organization identifiers. - ---- - -## Domain modules - -All modules live under `libs/core/src/modules/`. Apps import them directly from `@core/modules/`. - -| Module | Description | -|--------|-------------| -| `accounts` | Loads `Account` records with full relations (agent, grid, org, wallet, member). | -| `agents` | Queries agents by grid; loads with account, grid, and wallet relations. | -| `api-keys` | Resolves API keys with associated account relations. | -| `calin` | HTTP client for CALIN v1/v2 and STS token APIs, with retry logic and dev alerts. | -| `connections` | Raw SQL reporting queries — public connections, women impacted, etc. | -| `customers` | Reporting queries for customer groupings. | -| `dcus` | DCU lookups by id, external reference, and system. | -| `energy-tracking` | Queries `grid_business_snapshot_1_d` on TimescaleDB using `time_bucket_gapfill` for energy reporting. | -| `grids` | Grid queries filtered by reporting flags, organization, etc. | -| `issues` | Issue records with meter, customer, and grid relations. | -| `loki` | Winston + Loki transport (backing `CoreLoggerModule`). | -| `make` | Rate-limited POST client to Make.com scenario webhook URLs. | -| `meter-commissionings` | Loads commissioning records by id or session lock. | -| `metering-hardware-imports` | Import records and batch status queries via raw SQL. | -| `metering-hardware-install-sessions` | Install session lookups by id or ids. | -| `meters` | Core meter queries — by external reference, grid, commissioning path. | -| `mppts` | Syncs MPPTs from Victron and Supabase; provides MPPT service used across apps. | -| `notification-parameters` | CRUD stubs for per-grid notification configuration. | -| `orders` | Order/top-up queries with wallet and customer paths. | -| `organizations` | Organization entity. | -| `payouts` | Payout queries by organization or ids. | -| `routers` | Router lookups by grid, external system, and ids. | -| `software-dev-alert` | Sends developer alerts via Make.com (Telegram) and optionally email. | -| `solcast-cache` | DB-backed cache for Solcast API responses, keyed by geo/install parameters. | -| `spending` | Revenue and spending queries via Supabase RPCs and raw SQL. | -| `victron` | Victron VRM API client — JWT auth, rate limiting, site stats, and forecast helpers. | - -**Entity-only modules** (no service logic, provide TypeORM entities): `bank-accounts`, `banks`, `connection-requested-meters`, `directive-*`, `directives`, `loans`, `members`, `meter-credit-transfers`, `notes`, `notifications`, `poles`, `transactions`, `ussd-sessions`, `ussd-session-hops`, `wallets`. - ---- - -## Usage - -Apps import shared modules at the root level in their `app.module.ts`: - -```typescript -import { CoreTypeOrmModule, GlobalSupabaseModule, CoreLoggerModule } from '@core'; -import { CoreMetersModule } from '@core/modules/meters/meters.module'; -``` - -Feature modules then inject services from `@core` as normal NestJS providers. - ---- - -## License - -This project is licensed under the [Mozilla Public License 2.0](https://www.mozilla.org/MPL/2.0/). See the [`LICENSE`](../../LICENSE) file at the repository root. +Run `nx test core` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/core/eslint.config.mjs b/libs/core/eslint.config.mjs new file mode 100644 index 0000000..eb7ca0a --- /dev/null +++ b/libs/core/eslint.config.mjs @@ -0,0 +1,28 @@ +import baseConfig from "../../eslint.config.mjs"; + +export default [ + ...baseConfig, + { + files: [ + "**/*.json" + ], + rules: { + "@nx/dependency-checks": [ + "error", + { + ignoredFiles: [ + "{projectRoot}/eslint.config.{js,cjs,mjs,ts,cts,mts}" + ] + } + ] + }, + languageOptions: { + parser: await import("jsonc-eslint-parser") + } + }, + { + ignores: [ + "**/out-tsc" + ] + } +]; diff --git a/libs/core/jest.config.cts b/libs/core/jest.config.cts new file mode 100644 index 0000000..52ae915 --- /dev/null +++ b/libs/core/jest.config.cts @@ -0,0 +1,34 @@ +/* eslint-disable */ +const { readFileSync } = require('fs') + +// Reading the SWC compilation config for the spec files +const swcJestConfig = JSON.parse( + readFileSync(`${__dirname}/.spec.swcrc`, 'utf-8') +); + +// Disable .swcrc look-up by SWC core because we're passing in swcJestConfig ourselves +swcJestConfig.swcrc = false; + +module.exports = { + displayName: 'core', + preset: '../../jest.preset.js', + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': ['@swc/jest', swcJestConfig] + }, + moduleFileExtensions: ['ts', 'js', 'html'], + // Libs: unit only under test/unit/ (integration/e2e live on host apps). + testMatch: [ '/test/unit/**/*.(spec|test).ts' ], + // Source uses explicit `.js` extensions on relative imports (required by the NodeNext + // module resolution used for the real build); strip them so Jest's resolver finds the + // sibling `.ts` file instead. + moduleNameMapper: { + // In-package subpath imports (package.json "imports"); strip `.js` for TS sources. + '^#config/(.*)\\.js$': '/src/config/$1', + '^#modules/(.*)\\.js$': '/src/modules/$1', + '^#types/(.*)\\.js$': '/src/types/$1', + // Source uses explicit `.js` extensions on relative imports (NodeNext); strip for Jest. + '^(\\.{1,2}/.*)\\.js$': '$1', + }, + coverageDirectory: 'test-output/jest/coverage' +}; diff --git a/libs/core/package.json b/libs/core/package.json new file mode 100644 index 0000000..fc4c94c --- /dev/null +++ b/libs/core/package.json @@ -0,0 +1,72 @@ +{ + "name": "@nxt/core", + "version": "0.0.1", + "license": "MPL-2.0", + "authors": [ + { + "name": "Bobby Bol", + "email": "studio.innua@gmail.com" + }, + { + "name": "Tommaso Girotto", + "email": "tommaso.girotto91@gmail.com" + } + ], + "private": true, + "type": "module", + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "imports": { + "#config/*.js": { + "source": "./src/config/*.ts", + "default": "./dist/config/*.js" + }, + "#modules/*.js": { + "source": "./src/modules/*.ts", + "default": "./dist/modules/*.js" + }, + "#types/*.js": { + "source": "./src/types/*.ts", + "default": "./dist/types/*.js" + } + }, + "exports": { + "./package.json": "./package.json", + ".": { + "source": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + }, + "./config": { + "source": "./src/config/index.ts", + "types": "./dist/config/index.d.ts", + "import": "./dist/config/index.js", + "default": "./dist/config/index.js" + }, + "./types/supabase-types": { + "source": "./src/types/supabase-types.ts", + "types": "./dist/types/supabase-types.d.ts", + "import": "./dist/types/supabase-types.js", + "default": "./dist/types/supabase-types.js" + }, + "./types/supabase-types-adjusted": { + "source": "./src/types/supabase-types-adjusted.ts", + "types": "./dist/types/supabase-types-adjusted.d.ts", + "import": "./dist/types/supabase-types-adjusted.js", + "default": "./dist/types/supabase-types-adjusted.js" + } + }, + "nx": { + "name": "core" + }, + "dependencies": { + "@nestjs/axios": "^4.0.1", + "@nestjs/common": "^11.0.0", + "@supabase/supabase-js": "^2.103.3", + "class-validator": "^0.15.1", + "tslib": "^2.3.0", + "zod": "^4.4.3" + } +} diff --git a/libs/core/src/config/deep-freeze.ts b/libs/core/src/config/deep-freeze.ts new file mode 100644 index 0000000..4f9944a --- /dev/null +++ b/libs/core/src/config/deep-freeze.ts @@ -0,0 +1,23 @@ +/** + * Recursively freezes a plain object or array tree. Intended for JSON-derived config values where + * nested objects must be immutable, not just the root. + */ +export function deepFreeze(value: T, seen = new WeakSet()): T { + if (value === null || typeof value !== 'object') { + return value; + } + + if (seen.has(value)) { + return value; + } + seen.add(value); + + for (const key of Reflect.ownKeys(value)) { + const nested = (value as Record)[key]; + if (nested !== null && typeof nested === 'object') { + deepFreeze(nested, seen); + } + } + + return Object.freeze(value); +} diff --git a/libs/core/src/config/index.ts b/libs/core/src/config/index.ts new file mode 100644 index 0000000..6a48e24 --- /dev/null +++ b/libs/core/src/config/index.ts @@ -0,0 +1,29 @@ +import type { NxtConfig } from './schema.js'; + +let currentConfig: NxtConfig | undefined; + +/** + * Stores the active configuration. Used internally by {@link loadConfig} after validation, and + * directly by tests as the `setConfig(testConfig)` override pattern (ADR-007 decision 3). + */ +export function setConfig(config: NxtConfig): void { + currentConfig = config; +} + +/** + * Returns the active configuration. Throws if called before {@link loadConfig} (or + * `setConfig()` in tests) has run — there is no implicit default (ADR-007 decision 3). + */ +export function getConfig(): NxtConfig { + if (currentConfig === undefined) { + throw new Error( + 'getConfig() was called before configuration was loaded. Call loadConfig() in main.ts (or setConfig() in tests) first.', + ); + } + return currentConfig; +} + +export { loadConfig } from './loader.js'; +export type { LoadConfigOptions } from './loader.js'; +export type { NxtConfig } from './schema.js'; +export { requireEnv } from './require-env.js'; diff --git a/libs/core/src/config/loader.ts b/libs/core/src/config/loader.ts new file mode 100644 index 0000000..55f5723 --- /dev/null +++ b/libs/core/src/config/loader.ts @@ -0,0 +1,96 @@ +import { existsSync, readFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { deepFreeze } from './deep-freeze.js'; +import { setConfig } from './index.js'; +import { nxtConfigSchema, type NxtConfig } from './schema.js'; + +const DEFAULT_CONFIG_FILENAME = 'config.default.json'; + +export interface LoadConfigOptions { + /** + * Overrides the resolved path to the bundled default config. Intended for tests exercising + * precedence order without depending on the real repo-root artifact. + */ + defaultConfigPath?: string; +} + +/** + * Resolves, parses, validates, freezes, and stores the host configuration. Must run + * before `NestFactory.create` (ADR-007 decision 3) — nothing may read `getConfig()` earlier. + * + * Precedence: `NXT_CONFIG_JSON` (inline) → `NXT_CONFIG_URL` (fetch — reserved slot, not + * implemented, see ADR-007 decision 4) → `NXT_CONFIG_PATH` (file) → bundled + * `config.default.json`. + */ +export function loadConfig(options: LoadConfigOptions = {}): NxtConfig { + const rawJson = resolveRawConfig(options); + const config = parseConfig(rawJson); + const frozenConfig = deepFreeze(config); + setConfig(frozenConfig); + return frozenConfig; +} + +function resolveRawConfig(options: LoadConfigOptions): string { + const inlineJson = process.env.NXT_CONFIG_JSON; + if (inlineJson) { + return inlineJson; + } + + // NXT_CONFIG_URL (fetch) — reserved precedence slot for a future async config source + // (ADR-007 decision 4: CMS dump / object-store URL). Not implemented: no runtime check here + // by design — see the 002c decisions log (2026-07-14, Task 3). + + const configPath = process.env.NXT_CONFIG_PATH; + if (configPath) { + return readConfigFile(configPath); + } + + return readConfigFile(options.defaultConfigPath ?? resolveDefaultConfigPath()); +} + +function readConfigFile(path: string): string { + try { + return readFileSync(path, 'utf-8'); + } + catch (cause) { + throw new Error(`Failed to read config file at "${ path }": ${ (cause as Error).message }`, { cause }); + } +} + +function resolveDefaultConfigPath(): string { + const bundleDir = dirname(fileURLToPath(import.meta.url)); + const candidates = [ + join(bundleDir, DEFAULT_CONFIG_FILENAME), // copied alongside the built app (webpack asset) + join(process.cwd(), DEFAULT_CONFIG_FILENAME), // repo root — dev/test runs + ]; + + const found = candidates.find(candidate => existsSync(candidate)); + if (!found) { + throw new Error( + `Could not locate the bundled default config ("${ DEFAULT_CONFIG_FILENAME }"). Looked in: ${ candidates.join(', ') }`, + ); + } + return found; +} + +function parseConfig(rawJson: string): NxtConfig { + let candidate: unknown; + try { + candidate = JSON.parse(rawJson); + } + catch (cause) { + throw new Error(`Failed to parse config JSON: ${ (cause as Error).message }`, { cause }); + } + + const result = nxtConfigSchema.safeParse(candidate); + if (!result.success) { + const issues = result.error.issues + .map(issue => ` - ${ issue.path.join('.') || '(root)' }: ${ issue.message }`) + .join('\n'); + throw new Error(`Invalid configuration:\n${ issues }`); + } + + return result.data; +} diff --git a/libs/core/src/config/require-env.ts b/libs/core/src/config/require-env.ts new file mode 100644 index 0000000..1adde6b --- /dev/null +++ b/libs/core/src/config/require-env.ts @@ -0,0 +1,12 @@ +/** + * Reads a required environment variable, throwing a clear, fail-fast error if it is unset or + * empty. Co-located pattern for Tier-2 adapter `forRoot`/constructors and Foundation providers + * (ADR-007 decision 9, layer 2) — e.g. `SupabaseService` admin client secrets. + */ +export function requireEnv(name: string): string { + const value = process.env[name]; + if (!value) { + throw new Error(`MISSING ${ name }`); + } + return value; +} diff --git a/libs/core/src/config/schema.ts b/libs/core/src/config/schema.ts new file mode 100644 index 0000000..320ed35 --- /dev/null +++ b/libs/core/src/config/schema.ts @@ -0,0 +1,31 @@ +import { z } from 'zod'; + +/** + * Category (C) presentation/content — the only browser-safe subtree (ADR-007 decision 5). + * + * Category (B) data-references (`deployment.adminOrganizationId` / `systemWalletId`) were + * dropped in 002d — see ADR-007 Amendment 2026-07-15 §B. Admin org is DB-native; system + * wallet returns under Payments capability config when that capability is imported. + */ +const publicConfigSchema = z.object({ + platformName: z.string(), +}).strict(); + +/** + * Category (A) Tier-1/Tier-2 flags. Grows with capability imports — do not speculate ahead. + * Empty until the first real Tier-1 capability lands (demo scaffolding removed in 002d Task 4). + */ +const capabilitiesSchema = z.object({}).strict(); + +/** Category (A) Tier-3 optional augmentations. Grows with capability imports. */ +const integrationsSchema = z.object({}).strict(); + +export const nxtConfigSchema = z.object({ + $schema: z.string().optional(), + $schemaVersion: z.literal('1'), + public: publicConfigSchema, + capabilities: capabilitiesSchema.default({}), + integrations: integrationsSchema.default({}), +}).strict(); + +export type NxtConfig = z.infer; diff --git a/libs/core/src/constants.ts b/libs/core/src/constants.ts index 8f5a3e4..5813f52 100644 --- a/libs/core/src/constants.ts +++ b/libs/core/src/constants.ts @@ -1,24 +1,2 @@ -export const CALIN_API_FAIL = 'CALIN_API_FAIL'; // @TODO :: Delete - -export const NXT_ORG_ID = 2; - -export const BANKING_SYSTEM_WALLET_ID = 968; - -export const SUPABASE_QUERY_LIMIT = 10000; - -// export const GRIDS_USING_METER_INTERACTIONS_FOR_V1 = [ -// 60, // Software Test Grid -// 3, // Ogheye -// 17, // Belel - -// 21, // Akinsolu -// 5, // Demo Community -// 40, // Evbuwa -// 41, // Iguemokhua -// 57, // Iyemero -// 47, // Kosubosu -// 38, // Matari -// 13, // Ogbinbiri -// // 42, // Okpokunou -> Not yet because external testing -// // 46, // Umuolu -> This is V2 and LoRaWAN mixed -// ]; +/** Default row cap for Supabase `.limit()` calls — kept as a plain constant (002d Task 4). */ +export const SUPABASE_QUERY_LIMIT = 10_000; diff --git a/libs/core/src/index.ts b/libs/core/src/index.ts index cc4f2c8..d4a272f 100644 --- a/libs/core/src/index.ts +++ b/libs/core/src/index.ts @@ -1,5 +1,9 @@ -export { CoreTypeOrmModule } from './modules/core-typeorm.module'; -export { GlobalHttpModule } from './modules/global-http-module'; -export { GlobalSupabaseModule } from './modules/supabase.module'; -export { CoreLoggerModule } from './modules/logger-module'; -export { CorePgModule } from './modules/core-pg'; +export { SUPABASE_QUERY_LIMIT } from './constants.js'; +export { CreateCustomerDto } from './modules/customers/dto/create-customer.dto.js'; +export { GlobalHttpModule } from './modules/global-http-module.js'; +export { GlobalLoggerModule } from './modules/logger/logger.module.js'; +export { + GlobalSupabaseModule, + SupabaseService, + throwSupabaseError, +} from './modules/supabase/supabase.module.js'; diff --git a/libs/core/src/modules/accounts/accounts.service.ts b/libs/core/src/modules/accounts/accounts.service.ts deleted file mode 100644 index 84e1089..0000000 --- a/libs/core/src/modules/accounts/accounts.service.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; - -import { Account } from './entities/account.entity'; - -@Injectable() -export class AccountsService { - constructor( - @InjectRepository(Account) - protected accountRepository: Repository, - ) { } - - findOne(id: number) { - return this.accountRepository.findOne({ - relations: { - agent: { - grid: { - organization: true, - }, - wallet: true, - }, - organization: { - wallet: true, - }, - member: { - busy_commissioning: true, - }, - }, - where: { id }, - }); - } -} diff --git a/libs/core/src/modules/accounts/entities/account.entity.ts b/libs/core/src/modules/accounts/entities/account.entity.ts deleted file mode 100644 index 9b24e18..0000000 --- a/libs/core/src/modules/accounts/entities/account.entity.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { Column, DeleteDateColumn, Entity, JoinColumn, ManyToOne, OneToMany, OneToOne } from 'typeorm'; -import { CoreEntity } from '@core/types/core-entity'; -import { Agent } from '@core/modules/agents/entities/agent.entity'; -import { Customer } from '@core/modules/customers/entities/customer.entity'; -import { UssdSession } from '@core/modules/ussd-sessions/entities/ussd-session.entity'; -import { Organization } from '@core/modules/organizations/entities/organization.entity'; -import { Order } from '@core/modules/orders/entities/order.entity'; -import { DirectiveBatch } from '../../directive-batches/entities/directive-batch.entity'; -// import { Audit } from '@core/modules/audits/entities/audit.entity'; -import { Directive } from '@core/modules/directives/entities/directive.entity'; -import { MeteringHardwareInstallSession } from '@core/modules/metering-hardware-install-sessions/entities/metering-hardware-install-session.entity'; -import { ApiKey } from '@core/modules/api-keys/entities/api-key.entity'; -import { Note } from '@core/modules/notes/entities/note.entity'; -import { Payout } from '@core/modules/payouts/entities/payout.entity'; -import { MeterCreditTransfer } from '@core/modules/meter-credit-transfers/entities/meter-credit-transfer.entity'; -import { Notification } from '@core/modules/notifications/entities/notification.entity'; -import { Member } from '@core/modules/members/entities/member.entity'; - -@Entity('accounts') -export class Account extends CoreEntity { - @OneToOne(() => Agent, agent => agent.account) - agent?: Agent; - - @OneToOne(() => Customer, customer => customer.account) - customer?: Customer; - - @OneToOne(() => Member, member => member.account) - member?: Member; - - @Column('varchar', { nullable: true }) - supabase_id?: string; - - @Column('varchar', { nullable: true }) - full_name?: string; - - @Column('varchar', { nullable: true }) - email?: string; - - @Column('varchar', { nullable: true }) - phone?: string; - - @Column('varchar', { nullable: true, unique: true }) - telegram_id?: string; - - @Column('varchar', { nullable: true, unique: true }) - telegram_link_token?: string; - - @ManyToOne(() => Organization, organization => organization.accounts) - @JoinColumn({ name: 'organization_id' }) - organization?: Organization; - - @OneToMany(() => UssdSession, ussdSession => ussdSession.account) - ussd_sessions?: UssdSession[]; - - @OneToMany(() => Order, order => order.author) - orders?: Order[]; - - @OneToMany(() => DirectiveBatch, tou_rule => tou_rule.author) - tou_rules?: DirectiveBatch[]; - - @DeleteDateColumn({ type: 'timestamp', precision: 3, nullable: true }) - deleted_at?: Date; - - // @OneToMany(() => Audit, audit => audit.author) - // audits?: Audit[]; - - @OneToMany(() => Directive, directive => directive.author) - directives?: Directive[]; - - @OneToMany(() => MeteringHardwareInstallSession, metering_hardware_install_session => metering_hardware_install_session.author) - metering_hardware_install_sessions?: MeteringHardwareInstallSession[]; - - @OneToMany(() => MeterCreditTransfer, meter_credit_transfer => meter_credit_transfer.author) - meter_credit_transfers?: MeterCreditTransfer[]; - - @OneToMany(() => ApiKey, api_key => api_key.account) - api_keys?: ApiKey[]; - - @OneToMany(() => Note, note => note.author) - notes?: Note[]; - - @OneToMany(() => Payout, payout => payout.approved_by) - payouts?: Payout[]; - - @OneToMany(() => Notification, notification => notification.account) - notifications?: Notification[]; -} diff --git a/libs/core/src/modules/api-keys/api-keys.service.ts b/libs/core/src/modules/api-keys/api-keys.service.ts deleted file mode 100644 index 7161fac..0000000 --- a/libs/core/src/modules/api-keys/api-keys.service.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { ApiKey } from './entities/api-key.entity'; -import { Repository } from 'typeorm'; -import { InjectRepository } from '@nestjs/typeorm'; - -@Injectable() -export class ApiKeysService { - constructor( - @InjectRepository(ApiKey) - protected readonly apiKeyRepository: Repository, - ) { } - - findByKeyAndIsLocked(key: string, isLocked: false) { - const qb = this.apiKeyRepository.createQueryBuilder('api_keys'); - - return qb - .leftJoinAndSelect('api_keys.account', 'account') - .leftJoinAndSelect('account.customer', 'customer') - .leftJoinAndSelect('account.agent', 'agent') - .leftJoinAndSelect('account.member', 'member') - .leftJoinAndSelect('account.organization', 'organization') - .where('api_keys.is_locked = :is_locked', { is_locked: isLocked }) - .andWhere('api_keys.key = :key', { key: key }) - .getOne(); - } -} diff --git a/libs/core/src/modules/api-keys/entities/api-key.entity.ts b/libs/core/src/modules/api-keys/entities/api-key.entity.ts deleted file mode 100644 index bcdeec2..0000000 --- a/libs/core/src/modules/api-keys/entities/api-key.entity.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { CoreEntity } from '@core/types/core-entity'; -import { Column, Entity, JoinColumn, ManyToOne } from 'typeorm'; -import { Account } from '@core/modules/accounts/entities/account.entity'; - -@Entity('api_keys') -export class ApiKey extends CoreEntity { - @ManyToOne(() => Account, author => author.api_keys) - @JoinColumn({ name: 'account_id' }) - account: Account; - - @Column('bool', { default: false }) - is_locked: boolean; - - @Column('varchar', { nullable: true, unique: true }) - key: string; -} diff --git a/libs/core/src/modules/bank-accounts/entities/bank-account.entity.ts b/libs/core/src/modules/bank-accounts/entities/bank-account.entity.ts deleted file mode 100644 index 2212deb..0000000 --- a/libs/core/src/modules/bank-accounts/entities/bank-account.entity.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Entity, JoinColumn, ManyToOne, OneToMany } from 'typeorm'; -import { Organization } from '@core/modules/organizations/entities/organization.entity'; -import { Bank } from '@core/modules/banks/entities/bank.entity'; -import { CoreEntity } from '@core/types/core-entity'; -import { Payout } from '@core/modules/payouts/entities/payout.entity'; - -@Entity('bank_accounts') -export class BankAccount extends CoreEntity { - account_number: string; - - @ManyToOne(() => Organization, organization => organization.bank_accounts) - @JoinColumn({ name: 'organization_id' }) - organization: Organization; - - @ManyToOne(() => Bank, bank => bank.bank_accounts) - @JoinColumn({ name: 'bank_id' }) - bank: Bank; - - @OneToMany(() => Payout, payouts => payouts.bank_account) - payouts: Payout[]; -} diff --git a/libs/core/src/modules/connection-requested-meters/entities/connection-requested-meter.entity.ts b/libs/core/src/modules/connection-requested-meters/entities/connection-requested-meter.entity.ts deleted file mode 100644 index c732a10..0000000 --- a/libs/core/src/modules/connection-requested-meters/entities/connection-requested-meter.entity.ts +++ /dev/null @@ -1,28 +0,0 @@ -// import { Entity, Column, ManyToOne, JoinColumn, DeleteDateColumn } from 'typeorm'; -// import { CoreEntity } from '@core/types/core-entity'; -// import { Connection } from '@core/modules/connections/entities/connection.entity'; -// import { MeterPhaseEnum, MeterTypeEnum } from '@core/types/supabase-types'; - -// @Entity('connection_requested_meters') -// export class ConnectionRequestedMeter extends CoreEntity { -// // @Column('enum', { enum: MeterTypeEnum }) -// @Column({ type: 'varchar' }) -// meter_type: MeterTypeEnum; - -// // @Column('enum', { enum: MeterPhaseEnum }) -// @Column({ type: 'varchar' }) -// meter_phase: MeterPhaseEnum; - -// @Column('float', { default: 0 }) -// fee: number; - -// @DeleteDateColumn({ precision: 3 }) -// deleted_at: Date; - -// @Column('int') -// connection_id: number; - -// @ManyToOne(() => Connection, connection => connection.connection_requested_meters) -// @JoinColumn({ name: 'connection_id' }) -// connection: Connection; -// } diff --git a/libs/core/src/modules/customers/dto/create-customer.dto.ts b/libs/core/src/modules/customers/dto/create-customer.dto.ts index 5c9887c..d54c9a4 100644 --- a/libs/core/src/modules/customers/dto/create-customer.dto.ts +++ b/libs/core/src/modules/customers/dto/create-customer.dto.ts @@ -1,11 +1,27 @@ -import { GenderEnum, GeneratorTypeEnum } from '@core/types/supabase-types'; -import { IsBoolean, IsEmail, IsIn, IsLatitude, IsLongitude, IsNotEmpty, IsNumber, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; -import { Constants } from '@core/types/supabase-types'; - +import { + IsBoolean, + IsEmail, + IsIn, + IsLatitude, + IsLongitude, + IsNotEmpty, + IsNumber, + IsOptional, + IsPhoneNumber, + IsString, +} from 'class-validator'; + +import { + Constants, + type GenderEnum, + type GeneratorTypeEnum, +} from '#types/supabase-types.js'; + +/** Shared create-customer payload (api user-admin + worker). */ export class CreateCustomerDto { @IsString() @IsNotEmpty() - full_name: string; + full_name!: string; @IsPhoneNumber() @IsOptional() @@ -24,10 +40,10 @@ export class CreateCustomerDto { longitude?: number; @IsBoolean() - is_hidden_from_reporting: boolean; + is_hidden_from_reporting!: boolean; @IsNumber() - grid_id: number; + grid_id!: number; @IsBoolean() @IsOptional() diff --git a/libs/core/src/modules/directive-watchdog-sessions/entities/directive-watchdog-session.entity.ts b/libs/core/src/modules/directive-watchdog-sessions/entities/directive-watchdog-session.entity.ts deleted file mode 100644 index 294409c..0000000 --- a/libs/core/src/modules/directive-watchdog-sessions/entities/directive-watchdog-session.entity.ts +++ /dev/null @@ -1,16 +0,0 @@ -// import { Column, Entity, OneToMany } from 'typeorm'; -// import { CoreEntity } from '@core/types/core-entity'; -// import { Directive } from '@core/modules/directives/entities/directive.entity'; -// import { Meter } from '@core/modules/meters/entities/meter.entity'; - -// @Entity('directive_watchdog_sessions') -// export class DirectiveWatchdogSession extends CoreEntity { -// @OneToMany(() => Directive, directive => directive.directive_watchdog_session) -// directives: Directive[]; - -// @Column('varchar', { nullable: true }) -// identifier: string; - -// @OneToMany(() => Meter, meter => meter.watchdog_session) -// meters: Meter[]; -// } diff --git a/libs/core/src/modules/directives/entities/directive.entity.ts b/libs/core/src/modules/directives/entities/directive.entity.ts deleted file mode 100644 index b02e46f..0000000 --- a/libs/core/src/modules/directives/entities/directive.entity.ts +++ /dev/null @@ -1,205 +0,0 @@ -import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, JoinColumn, Index, OneToMany, OneToOne } from 'typeorm'; - -import { Meter } from '@core/modules/meters/entities/meter.entity'; -import { DirectiveBatchExecution } from '../../directive-batch-executions/entities/directive-batch-execution.entity'; -import { Order } from '@core/modules/orders/entities/order.entity'; -import { Account } from '@core/modules/accounts/entities/account.entity'; -import { MeterCommissioning } from '@core/modules/meter-commissionings/entities/meter-commissioning.entity'; -// import { DirectiveWatchdogSession } from '@core/modules/directive-watchdog-sessions/entities/directive-watchdog-session.entity'; -import { MeterCreditTransfer } from '@core/modules/meter-credit-transfers/entities/meter-credit-transfer.entity'; - -import { DirectiveErrorEnum, DirectiveSpecialStatusEnum, DirectiveStatusEnum, DirectiveTypeEnum } from '@core/types/supabase-types'; - -@Entity('directives') -@Index([ 'meter', 'directive_status', 'created_at' ]) -@Index([ 'directive_status' ]) -@Index([ 'token' ]) -@Index([ 'execution_session' ]) -export class Directive { - @PrimaryGeneratedColumn('increment', { type: 'bigint' }) - id?: number; - - @Column({ type: 'timestamptz', default: () => 'now()', precision: 3 }) - created_at?: Date; - - @Column({ type: 'timestamptz', precision: 3, nullable: true }) - updated_at?: Date; - - // METER_COMMISSIONING = 1200 - // TOPUP/USER COMMANDS = 1000 - // TOU_RULE = 800 - // WATCHDOG = 600 - // BATCH = 400 - // RANDOM_CHECK = 100 - @Column('int', { default: 0 }) - directive_priority?: number; - - // @Column('enum', { enum: DirectiveStatusEnum, default: 'PENDING' }) - @Column({ type: 'varchar' }) - directive_status?: DirectiveStatusEnum; - - // @Column('enum', { enum: DirectiveStatusEnum, nullable: true }) - @Column({ type: 'varchar' }) - directive_status_a?: DirectiveStatusEnum; - - // @Column('enum', { enum: DirectiveStatusEnum, nullable: true }) - @Column({ type: 'varchar' }) - directive_status_b?: DirectiveStatusEnum; - - // @Column('enum', { enum: DirectiveStatusEnum, nullable: true }) - @Column({ type: 'varchar' }) - directive_status_c?: DirectiveStatusEnum; - - // @Column('enum', { enum: DirectiveTypeEnum }) - @Column({ type: 'varchar' }) - directive_type?: DirectiveTypeEnum; - - @Column('varchar', { nullable: true }) - external_reference?: string; - - @Column('varchar', { nullable: true }) - external_reference_a?: string; - - @Column('varchar', { nullable: true }) - external_reference_b?: string; - - @Column('varchar', { nullable: true }) - external_reference_c?: string; - - @Column('int', { nullable: true }) - power_down_count?: number; - - @Column('int', { nullable: true }) - power_limit?: number; - - @Column('int', { nullable: true }) - power_limit_should_be?: number; - - @Column('bool', { nullable: true }) - is_on?: boolean; - - @Column('float', { nullable: true }) - kwh?: number; - - @Column('float', { nullable: true }) - kwh_credit_available?: number; - - @Column('float', { nullable: true }) - voltage?: number; - - @Column('float', { nullable: true }) - voltage_a?: number; - - @Column('float', { nullable: true }) - voltage_b?: number; - - @Column('float', { nullable: true }) - voltage_c?: number; - - @Column('float', { nullable: true }) - power?: number; - - @Column('float', { nullable: true }) - power_a?: number; - - @Column('float', { nullable: true }) - power_b?: number; - - @Column('float', { nullable: true }) - power_c?: number; - - // @Column('enum', { enum: DirectiveSpecialStatusEnum, nullable: true }) - @Column({ type: 'varchar' }) - directive_special_status?: DirectiveSpecialStatusEnum; - - // @Column('enum', { enum: DirectiveErrorEnum, nullable: true }) - @Column({ type: 'varchar' }) - directive_error?: DirectiveErrorEnum; - - @Column('varchar', { nullable: true }) - token?: string; - - @Column('varchar', { nullable: true }) - meter_version?: string; - - @Column('varchar', { nullable: true }) - execution_session?: string; - - @Column({ type: 'timestamptz', precision: 3, nullable: true }) - status_last_checked_at?: Date; - - @Column('varchar', { nullable: true }) - status_check_lock_session?: string; - - // This is to avoid the content of the directive_batch_deprecated column to be erased - @Column('int', { nullable: true }) - directive_batch_deprecated_id?: number; - - @Column('boolean', { nullable: true }) - can_be_retried?: boolean; - - /** - * Relations - **/ - - @Column('int', { nullable: true }) - meter_id?: number; - - @ManyToOne(() => Meter, meter => meter.directives) - @JoinColumn({ name: 'meter_id' }) - meter?: Meter; - - @ManyToOne(() => Account, author => author.directives, { nullable: true }) - @JoinColumn({ name: 'author_id' }) - author?: Account; - - @ManyToOne(() => DirectiveBatchExecution, directiveBatchExecution => directiveBatchExecution.directives) - @JoinColumn({ name: 'directive_batch_execution_id' }) - directive_batch_execution?: DirectiveBatchExecution; - - @Column('int', { nullable: true }) - directive_batch_execution_id?: number; - - @ManyToOne(() => Order, order => order.directives) - @JoinColumn({ name: 'order_id' }) - order?: Order; - - @Column('int', { nullable: true }) - order_id?: number; - - @OneToOne(() => Order, order => order.directive) - last_order_attempt?: Order; - - @Column('int', { nullable: true }) - meter_commissioning_id?: number; - - @ManyToOne(() => MeterCommissioning, meterCommissioning => meterCommissioning.directives) - @JoinColumn({ name: 'meter_commissioning_id' }) - meter_commissioning?: MeterCommissioning; - - // @Column('int', { nullable: true }) - // directive_watchdog_session_id?: number; - - // @ManyToOne(() => DirectiveWatchdogSession, directive_watchdog_session => directive_watchdog_session.directives) - // @JoinColumn({ name: 'directive_watchdog_session_id' }) - // directive_watchdog_session?: DirectiveWatchdogSession; - - @ManyToOne(() => MeterCreditTransfer, meter_credit_transfer => meter_credit_transfer.directives) - @JoinColumn({ name: 'meter_credit_transfer_id' }) - meter_credit_transfer?: MeterCreditTransfer; - - @Column('int', { nullable: true }) - meter_credit_transfer_id?: number; - - // Retrying mechanism: a directive can point to another directive when it's retrying it. - // A directive can be retried multiple times. - @Column('int', { nullable: true }) - retry_of_directive_id?: number; - - @ManyToOne(() => Directive, directive => directive.directive_retries) - @JoinColumn({ name: 'retry_of_directive_id' }) - retry_of_directive?: Directive; - - @OneToMany(() => Directive, directive => directive.retry_of_directive) - directive_retries?: Directive[]; -} diff --git a/libs/core/src/modules/global-http-module.ts b/libs/core/src/modules/global-http-module.ts index 55e9667..bdb0af6 100644 --- a/libs/core/src/modules/global-http-module.ts +++ b/libs/core/src/modules/global-http-module.ts @@ -1,6 +1,7 @@ import { HttpModule } from '@nestjs/axios'; import { Global, Module } from '@nestjs/common'; +/** Global Nest `HttpModule` (`@nestjs/axios`) for Foundation outbound HTTP. */ @Global() @Module({ imports: [ HttpModule ], diff --git a/libs/core/src/modules/logger-module.ts b/libs/core/src/modules/logger-module.ts deleted file mode 100644 index ae64331..0000000 --- a/libs/core/src/modules/logger-module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Global, Module } from '@nestjs/common'; -import { LokiService } from './loki/loki.service'; - -@Global() -@Module({ - providers: [ LokiService ], - exports: [ LokiService ], -}) -export class CoreLoggerModule {} diff --git a/libs/core/src/modules/logger/logger.module.ts b/libs/core/src/modules/logger/logger.module.ts new file mode 100644 index 0000000..46cea40 --- /dev/null +++ b/libs/core/src/modules/logger/logger.module.ts @@ -0,0 +1,28 @@ +import { Global, Module } from '@nestjs/common'; + +/** + * Logging slot for Foundation hosts. + * + * **Current:** no-op. Hosts use Nest’s built-in `Logger` and free `console.*`. + * `nestjs-pino` is deferred: under `nx serve` (webpack), pino transport workers + * break pretty logging (“one line then silence”), and pino-http / pretty ANSI + * fight quick `console` debugging. + * + * Keep this module in each host’s `infrastructure` array so restore is one place. + * + * **Restore nestjs-pino:** + * 1. Re-add deps on `@nxt/core`: `nestjs-pino`, `pino-http`; optional `pino-pretty`. + * 2. Replace this module body with: + * `LoggerModule.forRootAsync({ useFactory: () => ({ pinoHttp: buildPinoHttpOptions() }) })` + * — option builders live in `./logger.options.ts` (`autoLogging: false`, + * `LOG_PRETTY` opt-in only). + * 3. Host `main.ts`: `bufferLogs: true` + `app.useLogger(app.get(Logger))` + * (re-export nestjs-pino `Logger` from `@nxt/core` if useful). + * 4. Services that need DI logging: `PinoLogger` + `setContext` (not + * `@InjectPinoLogger(name)` — token snapshot race with barrel/module order). + * 5. Before enabling pretty under webpack: externals / `pino-webpack-plugin` + * so transport workers resolve. + */ +@Global() +@Module({}) +export class GlobalLoggerModule {} diff --git a/libs/core/src/modules/logger/logger.options.ts b/libs/core/src/modules/logger/logger.options.ts new file mode 100644 index 0000000..aad3996 --- /dev/null +++ b/libs/core/src/modules/logger/logger.options.ts @@ -0,0 +1,79 @@ +import type { NxtConfig } from '#config/schema.js'; +import { getConfig } from '#config/index.js'; + +/** + * Soft default; override with `LOG_LEVEL` (e.g. `debug`, `warn`). + * Not fail-fast — logging must not block boot the way secrets do. + * + * Used when nestjs-pino is restored via `GlobalLoggerModule` — see that module’s JSDoc. + */ +const DEFAULT_LOG_LEVEL = 'info'; + +interface PinoTransportTarget { + target: string; + level?: string; + options?: Record; +} + +/** + * Pretty-print only when `LOG_PRETTY` is a truthy env string (`1`, `true`, `yes`). + * Off by default. Under webpack serve, pino transport workers need externals / + * pino-webpack-plugin or pretty will log once and then stall. + */ +export function shouldPrettyPrint(): boolean { + const prettyFlag = process.env.LOG_PRETTY?.trim().toLowerCase(); + return prettyFlag === '1' || prettyFlag === 'true' || prettyFlag === 'yes'; +} + +/** + * Assembles pino transport targets from deployment config + env. + * + * **Tier-3 slots (unbuilt — document only):** + * - `integrations.loki` + `LOKI_URL` — dedicated Loki push transport (later). + * Free path today: ship structured JSON on stdout and let the platform scrape it + * (“Loki-via-stdout”). + * - `integrations.sentry` + `SENTRY_DSN` — Sentry transport / SDK (later). + * + * Until those integration flags exist on the schema, this only adds the optional + * `pino-pretty` target when `LOG_PRETTY` is set. + */ +export function assembleLogTransports(config: NxtConfig = getConfig()): PinoTransportTarget[] { + const targets: PinoTransportTarget[] = []; + const { integrations } = config; + + // Tier-3 — Loki (deferred): when `integrations.loki` lands on the schema — + // if (integrations.loki?.enabled && process.env.LOKI_URL) { … push Loki target } + + // Tier-3 — Sentry (deferred): when `integrations.sentry` lands on the schema — + // if (integrations.sentry?.enabled && process.env.SENTRY_DSN) { … push Sentry target } + + void integrations; + + if (shouldPrettyPrint()) { + targets.push({ + target: 'pino-pretty', + options: { + colorize: true, + singleLine: true, + translateTime: 'SYS:standard', + }, + }); + } + + return targets; +} + +/** + * Options for nestjs-pino / pino-http — wire from `GlobalLoggerModule` on restore. + * HTTP auto-logging stays off permanently unless product requirements change. + */ +export function buildPinoHttpOptions(config: NxtConfig = getConfig()): Record { + const level = process.env.LOG_LEVEL?.trim() || DEFAULT_LOG_LEVEL; + const targets = assembleLogTransports(config); + + return { + level, + autoLogging: false, + ...(targets.length > 0 ? { transport: { targets } } : {}), + }; +} diff --git a/libs/core/src/modules/loki/loki.service.ts b/libs/core/src/modules/loki/loki.service.ts deleted file mode 100644 index 652c72c..0000000 --- a/libs/core/src/modules/loki/loki.service.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; -import { createLogger, transports, format, Logger } from 'winston'; -const LokiTransport = require('winston-loki'); - -@Injectable() -export class LokiService implements OnModuleInit{ - - // Client - private logger: Logger; - - onModuleInit() { - this.logger = createLogger({ - transports: [ new LokiTransport({ - host: process.env.LOKI_URL, - labels: { app: process.env.LOKI_APP_NAME }, - json: true, - format: format.json(), - replaceTimestamp: true, - onConnectionError: err => { - console.error(err); - }, - }), - new transports.Console({ - format: format.combine(format.simple(), format.colorize()), - }) ], - }); - } - - public log(level: 'error' | 'warn' | 'debug' | 'info' | 'log', message: string, labels?: any) { - if (!this.logger) return; - - this.logger.log(level, { message, labels: { content: JSON.stringify(labels) } }); - } -} diff --git a/libs/core/src/modules/members/entities/member.entity.ts b/libs/core/src/modules/members/entities/member.entity.ts deleted file mode 100644 index ce2a954..0000000 --- a/libs/core/src/modules/members/entities/member.entity.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Entity, Column, OneToOne, JoinColumn, ManyToOne } from 'typeorm'; -import { CoreEntity } from '@core/types/core-entity'; -import { Account } from '@core/modules/accounts/entities/account.entity'; -import { Grid } from '@core/modules/grids/entities/grid.entity'; -import { MemberTypeEnum } from '@core/types/supabase-types'; - -@Entity('members') -export class Member extends CoreEntity { - @Column('boolean', { default: false }) - subscribed_to_telegram_revenue_notifications?: boolean; - - @Column({ type: 'varchar' }) - member_type?: MemberTypeEnum; - - @OneToOne(() => Account) - @JoinColumn({ name: 'account_id' }) - account?: Account; - - @ManyToOne(() => Grid, grid => grid.being_commissioned_by) - @JoinColumn({ name: 'busy_commissioning_id' }) - busy_commissioning?: Grid; -} diff --git a/libs/core/src/modules/meter-credit-transfers/entities/meter-credit-transfer.entity.ts b/libs/core/src/modules/meter-credit-transfers/entities/meter-credit-transfer.entity.ts deleted file mode 100644 index 50bd30e..0000000 --- a/libs/core/src/modules/meter-credit-transfers/entities/meter-credit-transfer.entity.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Account } from '@core/modules/accounts/entities/account.entity'; -import { Directive } from '@core/modules/directives/entities/directive.entity'; -import { Meter } from '@core/modules/meters/entities/meter.entity'; -import { Order } from '@core/modules/orders/entities/order.entity'; -import { CoreEntity } from '@core/types/core-entity'; -import { CurrencyEnum, MeterCreditTransferStatusEnum } from '@core/types/supabase-types'; -import { Column, Entity, JoinColumn, ManyToOne, OneToMany, OneToOne } from 'typeorm'; - -@Entity('meter_credit_transfers') -export class MeterCreditTransfer extends CoreEntity { - - @Column('varchar', { nullable: true }) - lock_session?: string; - - @Column('float') - amount?: number; - - // @Column('enum', { enum: MeterCreditTransferStatusEnum, default: MeterCreditTransferStatusEnum.PENDING }) - @Column({ type: 'varchar' }) - meter_credit_transfer_status?: MeterCreditTransferStatusEnum; - - // Amount the sender meter is set to after the withdrawal is complete - @Column('float', { nullable: true }) - sender_meter_set_to_amount?: number; - - // @Column({ type: 'enum', enum: CurrencyEnum }) - @Column({ type: 'varchar' }) - currency?: CurrencyEnum; - - @ManyToOne(() => Meter, meter => meter.sender_meter_credit_transfers) - @JoinColumn({ name: 'sender_meter_id' }) - sender_meter?: Meter; - - @Column('int') - sender_meter_id?: number; - - @ManyToOne(() => Meter, meter => meter.receiver_meter_credit_transfers) - @JoinColumn({ name: 'receiver_meter_id' }) - receiver_meter?: Meter; - - @Column('int') - receiver_meter_id?: number; - - // A credit transfer involves many directives - @OneToMany(() => Directive, directive => directive.meter_credit_transfer) - directives?: Directive[]; - - // A credit transfer can only have one order - @OneToOne(() => Order, order => order.meter_credit_transfer) - order?: Order; - - // Author: who did it - @ManyToOne(() => Account, author => author.meter_credit_transfers) - @JoinColumn({ name: 'author_id' }) - author?: Account; -} diff --git a/libs/core/src/modules/organizations/entities/organization.entity.ts b/libs/core/src/modules/organizations/entities/organization.entity.ts deleted file mode 100644 index c5ccabe..0000000 --- a/libs/core/src/modules/organizations/entities/organization.entity.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Entity, Column, OneToMany, OneToOne } from 'typeorm'; -import { Grid } from '@core/modules/grids/entities/grid.entity'; -import { Wallet } from '@core/modules/wallets/entities/wallet.entity'; -import { CoreEntity } from '@core/types/core-entity'; -import { Account } from '@core/modules/accounts/entities/account.entity'; -// import { Audit } from '@core/modules/audits/entities/audit.entity'; -import { BankAccount } from '@core/modules/bank-accounts/entities/bank-account.entity'; -import { Notification } from '@core/modules/notifications/entities/notification.entity'; - -@Entity('organizations') -export class Organization extends CoreEntity { - - @Column('varchar') - name?: string; // Why optional if required? - - @Column('varchar', { nullable: true }) - formal_name?: string; // Why optional if required? - - @Column('varchar', { nullable: true }) - email?: string; - - @Column('varchar', { nullable: true }) - phone?: string; - - @Column('varchar', { nullable: true }) - address?: string; - - @Column('varchar', { nullable: true }) - epicollect_contract_survey_slug?: string; - - @Column('varchar', { nullable: true }) - epicollect_contract_survey_secret?: string; - - @Column('varchar', { nullable: true }) - epicollect_contract_survey_client_id?: string; - - @Column({ type: 'timestamptz', nullable: true, precision: 3 }) - epicollect_contract_last_sync_at?: Date; - - @Column('varchar', { nullable: true, unique: false }) - developer_group_telegram_chat_id?: string; - - /** - * Relations - **/ - - @OneToMany(() => Grid, grid => grid.organization) - grids?: Grid[]; - - @OneToMany(() => Account, account => account.organization) - accounts?: Account[]; - - @OneToOne(() => Wallet, wallet => wallet.organization) - wallet?: Wallet; - - @OneToMany(() => BankAccount, bank_account => bank_account.organization) - bank_accounts?: BankAccount[]; - - // @OneToMany(() => Audit, audit => audit.organization) - // audits?: Audit[]; - - @OneToMany(() => Notification, notification => notification.organization) - notifications?: Notification[]; -} diff --git a/libs/core/src/modules/supabase.module.ts b/libs/core/src/modules/supabase.module.ts deleted file mode 100644 index 3e62f75..0000000 --- a/libs/core/src/modules/supabase.module.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { Injectable, Global, Module, HttpException } from '@nestjs/common'; -import { createClient, PostgrestError } from '@supabase/supabase-js'; -import type { Database } from '@core/types/supabase-types-adjusted'; - -export const supabase = createClient(process.env.SUPABASE_API_URL, process.env.SUPABASE_SERVICE_ROLE_KEY); - -const isCloudflareHtmlError = (error: unknown): boolean => { - const msg = (error as any)?.message; - // Check if message exists, and if HTML - if(typeof msg !== 'string') return false; - const checkableMsg = msg.trim().toLowerCase(); - return checkableMsg.includes('html') || checkableMsg.includes('internal server error'); -}; - -export const throwSupabaseError = (error: unknown, status?: number): never => { - console.error('[SUPABASE RESPONSE ERROR]', { error, status }); - const message = typeof error === 'string' - ? error - : error instanceof Error - ? error.message - : (error as any)?.message ?? JSON.stringify(error); - throw new HttpException(message, status || 500); -}; - -@Injectable() -export class SupabaseService { - constructor() { } - - adminClient = supabase; - - HANDLE_RESPONSE_UNTYPED({ data, error, status }) { - if (error) { - if(isCloudflareHtmlError(error)) { - console.warn('Supabase service unavailable (5xx), returning null data'); - return null; - } - throwSupabaseError(error, status); - } - return data; - } - - handleResponse({ data, error, status }: { data: T, error: PostgrestError, status: number }): T { - if (error) { - // @TOCHECK :: See if this is sustainable, or whether we need to throw an error (and catch everywhere) - if(isCloudflareHtmlError(error)) { - console.warn('Supabase service unavailable (5xx), returning null data'); - return null; - } - throwSupabaseError(error, status); - } - return data; - } -} - -@Global() -@Module({ - providers: [ SupabaseService ], - exports: [ SupabaseService ], -}) -export class GlobalSupabaseModule { - constructor() { } -} diff --git a/libs/core/src/modules/supabase/supabase.errors.ts b/libs/core/src/modules/supabase/supabase.errors.ts new file mode 100644 index 0000000..ef43529 --- /dev/null +++ b/libs/core/src/modules/supabase/supabase.errors.ts @@ -0,0 +1,68 @@ +import { HttpException, ServiceUnavailableException } from '@nestjs/common'; + +/** Structural logger — avoids pnpm dual `@nestjs/common` peer identity mismatches. */ +interface ErrorLogger { + error(message: string, ...optionalParams: unknown[]): void; +} + +interface ErrorWithMessage { + message: unknown; +} + +function hasMessage(error: unknown): error is ErrorWithMessage { + return typeof error === 'object' && error !== null && 'message' in error; +} + +/** + * Detects HTML / generic 5xx bodies sometimes returned through the Data API + * (e.g. Cloudflare). Used to collapse gigantic HTML into a one-line log/response + * and map to HTTP 503 (infra unavailable — not an empty result set). + * + * Legacy soft-`null` was a panic brake when call sites were not throw-ready; OSS + * throws instead. Retries for this class of failure are a far-future optimization. + */ +function isCloudflareHtmlError(error: unknown): boolean { + const msg = hasMessage(error) ? error.message : undefined; + if (typeof msg !== 'string') { + return false; + } + const checkableMsg = msg.trim().toLowerCase(); + return checkableMsg.includes('html') || checkableMsg.includes('internal server error'); +} + +const CLOUDFLARE_HTML_SUMMARY = 'Supabase service unavailable (HTML/5xx body — likely Cloudflare)'; + +function resolveErrorMessage(error: unknown): string { + if (typeof error === 'string') { + return error; + } + if (error instanceof Error) { + return error.message; + } + if (hasMessage(error) && typeof error.message === 'string') { + return error.message; + } + return JSON.stringify(error); +} + +/** Logs then throws an HTTP exception — exported for auth and other Foundation consumers. */ +export function throwSupabaseError( + error: unknown, + status: number | undefined, + logger: ErrorLogger, +): never { + if (isCloudflareHtmlError(error)) { + // @TEMPORARY :: Logging the entire error so we can tighten the html check + console.info(error); + logger.error( + `[SUPABASE RESPONSE ERROR] status=${ status ?? 'unknown' } ${ CLOUDFLARE_HTML_SUMMARY }`, + ); + throw new ServiceUnavailableException(CLOUDFLARE_HTML_SUMMARY); + } + + const message = resolveErrorMessage(error); + logger.error( + `[SUPABASE RESPONSE ERROR] status=${ status ?? 'unknown' } ${ message }`, + ); + throw new HttpException(message, status ?? 500); +} diff --git a/libs/core/src/modules/supabase/supabase.module.ts b/libs/core/src/modules/supabase/supabase.module.ts new file mode 100644 index 0000000..0804e44 --- /dev/null +++ b/libs/core/src/modules/supabase/supabase.module.ts @@ -0,0 +1,91 @@ +import { + Global, + Injectable, + InternalServerErrorException, + Logger, + Module, +} from '@nestjs/common'; +import { createClient, type PostgrestError, type SupabaseClient } from '@supabase/supabase-js'; + +import { requireEnv } from '#config/require-env.js'; +import type { Database } from '#types/supabase-types-adjusted.js'; +import { throwSupabaseError } from './supabase.errors.js'; + +export { throwSupabaseError } from './supabase.errors.js'; + +interface SupabaseResponse { + data: T; + error: PostgrestError | null; + status: number; +} + +@Injectable() +export class SupabaseService { + readonly adminClient: SupabaseClient; + private readonly logger = new Logger(SupabaseService.name); + + constructor() { + this.adminClient = createClient( + requireEnv('SUPABASE_URL'), + requireEnv('SUPABASE_SECRET_KEY'), + { + auth: { + // Server-side admin client: no end-user session to persist or refresh. + persistSession: false, + autoRefreshToken: false, + }, + }, + ); + } + + /** + * Permissive: for `.maybeSingle()`, multi-row selects, etc. + * Errors (including Cloudflare HTML 5xx) throw — never soft-return null. + */ + HANDLE_RESPONSE_UNTYPED = ({ + data, + error, + status, + }: SupabaseResponse): unknown => { + if (error) { + throwSupabaseError(error, status, this.logger); + } + return data; + }; + + /** + * Permissive: for `.maybeSingle()`, multi-row selects, etc. + * `data` may be null when zero rows. Errors throw (no Cloudflare soft-null). + */ + handleResponse = ({ data, error, status }: SupabaseResponse): T => { + if (error) { + throwSupabaseError(error, status, this.logger); + } + return data; + }; + + /** + * Strict: use after `.single()` only — value or throw. + * PostgREST usually sets `error` on 0 rows; null `data` without error is treated as invariant break (500). + */ + handleSingle = ({ + data, + error, + status, + }: SupabaseResponse): NonNullable => { + if (error) { + throwSupabaseError(error, status, this.logger); + } + if (data == null) { + throw new InternalServerErrorException('Expected a single row, got none'); + } + return data; + }; +} + +@Global() +@Module({ + providers: [ SupabaseService ], + exports: [ SupabaseService ], +}) +export class GlobalSupabaseModule {} diff --git a/libs/core/src/types/supabase-types-adjusted.ts b/libs/core/src/types/supabase-types-adjusted.ts index eed79bf..033c421 100644 --- a/libs/core/src/types/supabase-types-adjusted.ts +++ b/libs/core/src/types/supabase-types-adjusted.ts @@ -1,4 +1,12 @@ -import { Database as OriginalDatabase } from './supabase-types'; +/** + * Augmented `Database` type — PostGIS `location_geom` as GeoJSON `Point` where the generator + * emits `unknown`. + * + * **Import convention (002d):** + * - `Database` → `@nxt/core/types/supabase-types-adjusted` (this module) + * - Enums, row/insert/update aliases, `Json`, etc. → `@nxt/core/types/supabase-types` + */ +import { Database as OriginalDatabase } from './supabase-types.js'; export type Database = OriginalDatabase & { public: OriginalDatabase['public'] & { @@ -7,17 +15,17 @@ export type Database = OriginalDatabase & { Row: OriginalDatabase['public']['Tables']['grids']['Row'] & { location_geom: { type: 'Point'; - coordinates: number[]; - } | null - } + coordinates: [ number, number ]; + } | null; + }; }; poles: OriginalDatabase['public']['Tables']['poles'] & { Row: OriginalDatabase['public']['Tables']['poles']['Row'] & { location_geom: { type: 'Point'; - coordinates: number[]; - } - } + coordinates: [ number, number ]; + }; + }; }; }; }; diff --git a/libs/core/src/types/supabase-types.ts b/libs/core/src/types/supabase-types.ts index 74d18a9..0bc7c86 100644 --- a/libs/core/src/types/supabase-types.ts +++ b/libs/core/src/types/supabase-types.ts @@ -1,11 +1,6 @@ export type Json = Record export type Database = { - // Allows to automatically instantiate createClient with right options - // instead of createClient(URL, KEY) - __InternalSupabase: { - PostgrestVersion: '12.2.3 (519615d)'; - }; public: { Tables: { accounts: { @@ -17,7 +12,7 @@ export type Database = { id: number; organization_id: number | null; phone: string | null; - supabase_id: string | null; + supabase_id: string; telegram_id: string | null; telegram_link_token: string | null; }; @@ -29,7 +24,7 @@ export type Database = { id?: number; organization_id?: number | null; phone?: string | null; - supabase_id?: string | null; + supabase_id: string; telegram_id?: string | null; telegram_link_token?: string | null; }; @@ -41,7 +36,7 @@ export type Database = { id?: number; organization_id?: number | null; phone?: string | null; - supabase_id?: string | null; + supabase_id?: string; telegram_id?: string | null; telegram_link_token?: string | null; }; @@ -106,21 +101,18 @@ export type Database = { account_id: number | null; created_at: string; id: number; - is_locked: boolean; key: string | null; }; Insert: { account_id?: number | null; created_at?: string; id?: number; - is_locked?: boolean; key?: string | null; }; Update: { account_id?: number | null; created_at?: string; id?: number; - is_locked?: boolean; key?: string | null; }; Relationships: [ @@ -266,69 +258,6 @@ export type Database = { }, ]; }; - autopilot_executions: { - Row: { - code: string | null; - created_at: string; - id: number; - input: Json | null; - output: Json | null; - queries: Json | null; - }; - Insert: { - code?: string | null; - created_at?: string; - id?: number; - input?: Json | null; - output?: Json | null; - queries?: Json | null; - }; - Update: { - code?: string | null; - created_at?: string; - id?: number; - input?: Json | null; - output?: Json | null; - queries?: Json | null; - }; - Relationships: []; - }; - bank_accounts: { - Row: { - bank_id: number | null; - created_at: string; - id: number; - organization_id: number | null; - }; - Insert: { - bank_id?: number | null; - created_at?: string; - id?: number; - organization_id?: number | null; - }; - Update: { - bank_id?: number | null; - created_at?: string; - id?: number; - organization_id?: number | null; - }; - Relationships: [ - { - foreignKeyName: 'FK_482543ba26483726aaa00d39174'; - columns: ['bank_id']; - isOneToOne: false; - referencedRelation: 'banks'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'FK_cc20105b139589c697648c925c3'; - columns: ['organization_id']; - isOneToOne: false; - referencedRelation: 'organizations'; - referencedColumns: ['id']; - }, - ]; - }; banks: { Row: { created_at: string; @@ -406,11 +335,9 @@ export type Database = { deleted_at: string | null; document_id: string | null; document_type: - | Database['public']['Enums']['id_document_type_enum'] - | null; + Database['public']['Enums']['id_document_type_enum'] | null; external_system: - | Database['public']['Enums']['external_system_enum'] - | null; + Database['public']['Enums']['external_system_enum'] | null; id: number; is_building_wired: boolean | null; is_commercial: boolean; @@ -430,11 +357,9 @@ export type Database = { deleted_at?: string | null; document_id?: string | null; document_type?: - | Database['public']['Enums']['id_document_type_enum'] - | null; + Database['public']['Enums']['id_document_type_enum'] | null; external_system?: - | Database['public']['Enums']['external_system_enum'] - | null; + Database['public']['Enums']['external_system_enum'] | null; id?: number; is_building_wired?: boolean | null; is_commercial: boolean; @@ -454,11 +379,9 @@ export type Database = { deleted_at?: string | null; document_id?: string | null; document_type?: - | Database['public']['Enums']['id_document_type_enum'] - | null; + Database['public']['Enums']['id_document_type_enum'] | null; external_system?: - | Database['public']['Enums']['external_system_enum'] - | null; + Database['public']['Enums']['external_system_enum'] | null; id?: number; is_building_wired?: boolean | null; is_commercial?: boolean; @@ -501,8 +424,7 @@ export type Database = { created_at: string; gender: Database['public']['Enums']['gender_enum'] | null; generator_owned: - | Database['public']['Enums']['generator_type_enum'] - | null; + Database['public']['Enums']['generator_type_enum'] | null; grid_id: number | null; id: number; is_hidden_from_reporting: boolean; @@ -518,8 +440,7 @@ export type Database = { created_at?: string; gender?: Database['public']['Enums']['gender_enum'] | null; generator_owned?: - | Database['public']['Enums']['generator_type_enum'] - | null; + Database['public']['Enums']['generator_type_enum'] | null; grid_id?: number | null; id?: number; is_hidden_from_reporting?: boolean; @@ -535,8 +456,7 @@ export type Database = { created_at?: string; gender?: Database['public']['Enums']['gender_enum'] | null; generator_owned?: - | Database['public']['Enums']['generator_type_enum'] - | null; + Database['public']['Enums']['generator_type_enum'] | null; grid_id?: number | null; id?: number; is_hidden_from_reporting?: boolean; @@ -574,8 +494,7 @@ export type Database = { dcus: { Row: { communication_protocol: - | Database['public']['Enums']['communication_protocol_enum'] - | null; + Database['public']['Enums']['communication_protocol_enum'] | null; created_at: string; external_reference: string; external_system: Database['public']['Enums']['external_system_enum']; @@ -586,13 +505,11 @@ export type Database = { last_metering_hardware_install_session_id: number | null; last_online_at: string | null; location_geom: unknown; - queue_buffer_length: number; rls_organization_id: number | null; }; Insert: { communication_protocol?: - | Database['public']['Enums']['communication_protocol_enum'] - | null; + Database['public']['Enums']['communication_protocol_enum'] | null; created_at?: string; external_reference: string; external_system: Database['public']['Enums']['external_system_enum']; @@ -603,13 +520,11 @@ export type Database = { last_metering_hardware_install_session_id?: number | null; last_online_at?: string | null; location_geom?: unknown; - queue_buffer_length?: number; rls_organization_id?: number | null; }; Update: { communication_protocol?: - | Database['public']['Enums']['communication_protocol_enum'] - | null; + Database['public']['Enums']['communication_protocol_enum'] | null; created_at?: string; external_reference?: string; external_system?: Database['public']['Enums']['external_system_enum']; @@ -620,7 +535,6 @@ export type Database = { last_metering_hardware_install_session_id?: number | null; last_online_at?: string | null; location_geom?: unknown; - queue_buffer_length?: number; rls_organization_id?: number | null; }; Relationships: [ @@ -647,823 +561,240 @@ export type Database = { }, ]; }; - device_logs: { + energy_cabins: { Row: { created_at: string; - device_id: number | null; + grid_id: number; id: number; - message: string | null; + location_geom: unknown; rls_organization_id: number | null; }; Insert: { created_at?: string; - device_id?: number | null; + grid_id: number; id?: number; - message?: string | null; + location_geom: unknown; rls_organization_id?: number | null; }; Update: { created_at?: string; - device_id?: number | null; + grid_id?: number; id?: number; - message?: string | null; + location_geom?: unknown; rls_organization_id?: number | null; }; Relationships: [ { - foreignKeyName: 'device_logs_device_id_fkey'; - columns: ['device_id']; + foreignKeyName: 'energy_cabins_rls_organization_id_fkey'; + columns: ['rls_organization_id']; isOneToOne: false; - referencedRelation: 'devices'; + referencedRelation: 'organizations'; referencedColumns: ['id']; }, { - foreignKeyName: 'device_logs_rls_organization_id_fkey'; - columns: ['rls_organization_id']; + foreignKeyName: 'FK_ba81ffc1506025a549849f06f36'; + columns: ['grid_id']; isOneToOne: false; - referencedRelation: 'organizations'; + referencedRelation: 'grids'; referencedColumns: ['id']; }, ]; }; - device_types: { + grids: { Row: { + commissioned_at: string | null; created_at: string; - external_system: - | Database['public']['Enums']['external_system_enum'] - | null; + current_weather: + Database['public']['Enums']['weather_type_enum'] | null; + default_fs_1_phase_connection_fee: number; + default_fs_3_phase_connection_fee: number; + default_hps_connection_fee: number; + deleted_at: string | null; + deployed_at: string | null; + feature_access_config: Json; + generation_external_gateway_id: string | null; + generation_external_site_id: string | null; + generation_external_system: Database['public']['Enums']['external_system_enum']; + generation_gateway_last_seen_at: string | null; id: number; - name: string | null; + identifier: number | null; + internal_telegram_group_chat_id: string | null; + internal_telegram_group_thread_id: string | null; + is_automatic_energy_generation_data_sync_enabled: boolean; + is_automatic_meter_energy_consumption_data_sync_enabled: boolean; + is_automatic_meter_install_enabled: boolean; + is_cabin_meter_credit_depleting: boolean; + is_dcu_connectivity_tracking_enabled: boolean; + is_energised_notification_enabled: boolean; + is_fs_control_rule_change_notification_enabled: boolean; + is_fs_on: boolean; + is_fs_on_notification_enabled: boolean; + is_fs_on_updated_at: string | null; + is_generation_managed_by_nxt_grid: boolean; + is_hidden_from_reporting: boolean; + is_hps_on: boolean; + is_hps_on_threshold_kw: number; + is_hps_on_updated_at: string | null; + is_metering_hardware_online_notification_enabled: boolean; + is_panel_cleaning_notification_enabled: boolean; + is_router_connectivity_tracking_enabled: boolean; + is_tariff_change_notification_enabled: boolean; + is_three_phase_supported: boolean; + is_upcoming_fs_control_rule_notification_enabled: boolean; + is_using_mobile_network: boolean; + is_using_vsat: boolean; + kwh: number; + kwh_per_battery_module: number | null; + kwh_tariff: number; + kwh_tariff_essential_service: number; + kwh_tariff_full_service: number; + kwp: number; + kwp_tariff: number; + lifeline_connection_days_threshold: number; + lifeline_connection_kwh_threshold: number; + location_geom: unknown; + meter_commissioning_initial_credit_kwh: number; + metering_external_system: Database['public']['Enums']['external_system_enum']; + monthly_rental: number; + name: string; + organization_id: number; + should_fs_be_on: boolean; + should_fs_be_on_updated_at: string | null; + telegram_config: Json | null; + telegram_notification_channel_invite_link: string | null; + telegram_response_path_token: string | null; + timezone: string; + walkthrough_external_id: string | null; }; Insert: { + commissioned_at?: string | null; created_at?: string; - external_system?: - | Database['public']['Enums']['external_system_enum'] - | null; + current_weather?: + Database['public']['Enums']['weather_type_enum'] | null; + default_fs_1_phase_connection_fee?: number; + default_fs_3_phase_connection_fee?: number; + default_hps_connection_fee?: number; + deleted_at?: string | null; + deployed_at?: string | null; + feature_access_config?: Json; + generation_external_gateway_id?: string | null; + generation_external_site_id?: string | null; + generation_external_system?: Database['public']['Enums']['external_system_enum']; + generation_gateway_last_seen_at?: string | null; id?: number; - name?: string | null; + identifier?: number | null; + internal_telegram_group_chat_id?: string | null; + internal_telegram_group_thread_id?: string | null; + is_automatic_energy_generation_data_sync_enabled?: boolean; + is_automatic_meter_energy_consumption_data_sync_enabled?: boolean; + is_automatic_meter_install_enabled?: boolean; + is_cabin_meter_credit_depleting?: boolean; + is_dcu_connectivity_tracking_enabled?: boolean; + is_energised_notification_enabled?: boolean; + is_fs_control_rule_change_notification_enabled?: boolean; + is_fs_on?: boolean; + is_fs_on_notification_enabled?: boolean; + is_fs_on_updated_at?: string | null; + is_generation_managed_by_nxt_grid?: boolean; + is_hidden_from_reporting?: boolean; + is_hps_on?: boolean; + is_hps_on_threshold_kw?: number; + is_hps_on_updated_at?: string | null; + is_metering_hardware_online_notification_enabled?: boolean; + is_panel_cleaning_notification_enabled?: boolean; + is_router_connectivity_tracking_enabled?: boolean; + is_tariff_change_notification_enabled?: boolean; + is_three_phase_supported?: boolean; + is_upcoming_fs_control_rule_notification_enabled?: boolean; + is_using_mobile_network?: boolean; + is_using_vsat?: boolean; + kwh?: number; + kwh_per_battery_module?: number | null; + kwh_tariff?: number; + kwh_tariff_essential_service?: number; + kwh_tariff_full_service?: number; + kwp?: number; + kwp_tariff?: number; + lifeline_connection_days_threshold?: number; + lifeline_connection_kwh_threshold?: number; + location_geom?: unknown; + meter_commissioning_initial_credit_kwh?: number; + metering_external_system?: Database['public']['Enums']['external_system_enum']; + monthly_rental?: number; + name: string; + organization_id: number; + should_fs_be_on?: boolean; + should_fs_be_on_updated_at?: string | null; + telegram_config?: Json | null; + telegram_notification_channel_invite_link?: string | null; + telegram_response_path_token?: string | null; + timezone?: string; + walkthrough_external_id?: string | null; }; Update: { + commissioned_at?: string | null; created_at?: string; - external_system?: - | Database['public']['Enums']['external_system_enum'] - | null; + current_weather?: + Database['public']['Enums']['weather_type_enum'] | null; + default_fs_1_phase_connection_fee?: number; + default_fs_3_phase_connection_fee?: number; + default_hps_connection_fee?: number; + deleted_at?: string | null; + deployed_at?: string | null; + feature_access_config?: Json; + generation_external_gateway_id?: string | null; + generation_external_site_id?: string | null; + generation_external_system?: Database['public']['Enums']['external_system_enum']; + generation_gateway_last_seen_at?: string | null; id?: number; - name?: string | null; - }; - Relationships: []; - }; - devices: { - Row: { - created_at: string; - device_type_id: number; - external_reference: string; - grid_id: number | null; - id: number; - nickname: string | null; - rls_organization_id: number | null; - values: Json; - }; - Insert: { - created_at?: string; - device_type_id: number; - external_reference: string; - grid_id?: number | null; - id?: number; - nickname?: string | null; - rls_organization_id?: number | null; - values?: Json; - }; - Update: { - created_at?: string; - device_type_id?: number; - external_reference?: string; - grid_id?: number | null; - id?: number; - nickname?: string | null; - rls_organization_id?: number | null; - values?: Json; - }; - Relationships: [ - { - foreignKeyName: 'devices_device_type_id_fkey'; - columns: ['device_type_id']; - isOneToOne: false; - referencedRelation: 'device_types'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'devices_grid_id_fkey'; - columns: ['grid_id']; - isOneToOne: false; - referencedRelation: 'grids'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'devices_rls_organization_id_fkey'; - columns: ['rls_organization_id']; - isOneToOne: false; - referencedRelation: 'organizations'; - referencedColumns: ['id']; - }, - ]; - }; - directive_batch_executions: { - Row: { - completed_at: string | null; - created_at: string; - directive_batch_id: number; - failed_count: number; - id: number; - pending_count: number; - processed_count: number; - processing_count: number; - qualified_at: string | null; - rls_organization_id: number | null; - successful_count: number; - total_count: number; - }; - Insert: { - completed_at?: string | null; - created_at?: string; - directive_batch_id: number; - failed_count?: number; - id?: number; - pending_count?: number; - processed_count?: number; - processing_count?: number; - qualified_at?: string | null; - rls_organization_id?: number | null; - successful_count?: number; - total_count?: number; - }; - Update: { - completed_at?: string | null; - created_at?: string; - directive_batch_id?: number; - failed_count?: number; - id?: number; - pending_count?: number; - processed_count?: number; - processing_count?: number; - qualified_at?: string | null; - rls_organization_id?: number | null; - successful_count?: number; - total_count?: number; - }; - Relationships: [ - { - foreignKeyName: 'directive_batch_executions_rls_organization_id_fkey'; - columns: ['rls_organization_id']; - isOneToOne: false; - referencedRelation: 'organizations'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'FK_7b0737005e7c358392d87ebb329'; - columns: ['directive_batch_id']; - isOneToOne: false; - referencedRelation: 'directive_batches'; - referencedColumns: ['id']; - }, - ]; - }; - directive_batches: { - Row: { - author_id: number | null; - created_at: string; - directive_type: - | Database['public']['Enums']['directive_type_enum'] - | null; - execution_bucket: number | null; - fs_command: - | Database['public']['Enums']['fs_command_type_enum'] - | null; - grid_id: number | null; - hour: number; - id: number; - is_deleted: boolean; - is_repeating: boolean; - lock_session: string | null; - minute: number; - rls_organization_id: number | null; - task_type: - | Database['public']['Enums']['meter_interaction_type_enum'] - | null; - updated_at: string | null; - }; - Insert: { - author_id?: number | null; - created_at?: string; - directive_type?: - | Database['public']['Enums']['directive_type_enum'] - | null; - execution_bucket?: number | null; - fs_command?: - | Database['public']['Enums']['fs_command_type_enum'] - | null; - grid_id?: number | null; - hour: number; - id?: number; - is_deleted?: boolean; - is_repeating?: boolean; - lock_session?: string | null; - minute: number; - rls_organization_id?: number | null; - task_type?: - | Database['public']['Enums']['meter_interaction_type_enum'] - | null; - updated_at?: string | null; - }; - Update: { - author_id?: number | null; - created_at?: string; - directive_type?: - | Database['public']['Enums']['directive_type_enum'] - | null; - execution_bucket?: number | null; - fs_command?: - | Database['public']['Enums']['fs_command_type_enum'] - | null; - grid_id?: number | null; - hour?: number; - id?: number; - is_deleted?: boolean; - is_repeating?: boolean; - lock_session?: string | null; - minute?: number; - rls_organization_id?: number | null; - task_type?: - | Database['public']['Enums']['meter_interaction_type_enum'] - | null; - updated_at?: string | null; - }; - Relationships: [ - { - foreignKeyName: 'directive_batches_rls_organization_id_fkey'; - columns: ['rls_organization_id']; - isOneToOne: false; - referencedRelation: 'organizations'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'FK_5c44fae05dc443720f62664e563'; - columns: ['grid_id']; - isOneToOne: false; - referencedRelation: 'grids'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'FK_6e26cb6e966f484f0f897127c84'; - columns: ['author_id']; - isOneToOne: false; - referencedRelation: 'accounts'; - referencedColumns: ['id']; - }, - ]; - }; - directive_watchdog_sessions: { - Row: { - created_at: string; - id: number; - identifier: string | null; - }; - Insert: { - created_at?: string; - id?: number; - identifier?: string | null; - }; - Update: { - created_at?: string; - id?: number; - identifier?: string | null; - }; - Relationships: []; - }; - directives: { - Row: { - author_id: number | null; - can_be_retried: boolean | null; - created_at: string; - directive_batch_deprecated_id: number | null; - directive_batch_execution_id: number | null; - directive_error: - | Database['public']['Enums']['directive_error_enum'] - | null; - directive_priority: number; - directive_special_status: - | Database['public']['Enums']['directive_special_status_enum'] - | null; - directive_status: Database['public']['Enums']['directive_status_enum']; - directive_status_a: - | Database['public']['Enums']['directive_status_enum'] - | null; - directive_status_b: - | Database['public']['Enums']['directive_status_enum'] - | null; - directive_status_c: - | Database['public']['Enums']['directive_status_enum'] - | null; - directive_type: Database['public']['Enums']['directive_type_enum']; - directive_watchdog_session_id: number | null; - execution_session: string | null; - external_reference: string | null; - external_reference_a: string | null; - external_reference_b: string | null; - external_reference_c: string | null; - id: number; - is_on: boolean | null; - kwh: number | null; - kwh_credit_available: number | null; - meter_commissioning_id: number | null; - meter_credit_transfer_id: number | null; - meter_id: number | null; - meter_version: string | null; - order_id: number | null; - power: number | null; - power_a: number | null; - power_b: number | null; - power_c: number | null; - power_down_count: number | null; - power_limit: number | null; - power_limit_should_be: number | null; - retry_of_directive_id: number | null; - rls_organization_id: number | null; - status_check_lock_session: string | null; - status_last_checked_at: string | null; - token: string | null; - updated_at: string | null; - voltage: number | null; - voltage_a: number | null; - voltage_b: number | null; - voltage_c: number | null; - }; - Insert: { - author_id?: number | null; - can_be_retried?: boolean | null; - created_at?: string; - directive_batch_deprecated_id?: number | null; - directive_batch_execution_id?: number | null; - directive_error?: - | Database['public']['Enums']['directive_error_enum'] - | null; - directive_priority?: number; - directive_special_status?: - | Database['public']['Enums']['directive_special_status_enum'] - | null; - directive_status?: Database['public']['Enums']['directive_status_enum']; - directive_status_a?: - | Database['public']['Enums']['directive_status_enum'] - | null; - directive_status_b?: - | Database['public']['Enums']['directive_status_enum'] - | null; - directive_status_c?: - | Database['public']['Enums']['directive_status_enum'] - | null; - directive_type: Database['public']['Enums']['directive_type_enum']; - directive_watchdog_session_id?: number | null; - execution_session?: string | null; - external_reference?: string | null; - external_reference_a?: string | null; - external_reference_b?: string | null; - external_reference_c?: string | null; - id?: number; - is_on?: boolean | null; - kwh?: number | null; - kwh_credit_available?: number | null; - meter_commissioning_id?: number | null; - meter_credit_transfer_id?: number | null; - meter_id?: number | null; - meter_version?: string | null; - order_id?: number | null; - power?: number | null; - power_a?: number | null; - power_b?: number | null; - power_c?: number | null; - power_down_count?: number | null; - power_limit?: number | null; - power_limit_should_be?: number | null; - retry_of_directive_id?: number | null; - rls_organization_id?: number | null; - status_check_lock_session?: string | null; - status_last_checked_at?: string | null; - token?: string | null; - updated_at?: string | null; - voltage?: number | null; - voltage_a?: number | null; - voltage_b?: number | null; - voltage_c?: number | null; - }; - Update: { - author_id?: number | null; - can_be_retried?: boolean | null; - created_at?: string; - directive_batch_deprecated_id?: number | null; - directive_batch_execution_id?: number | null; - directive_error?: - | Database['public']['Enums']['directive_error_enum'] - | null; - directive_priority?: number; - directive_special_status?: - | Database['public']['Enums']['directive_special_status_enum'] - | null; - directive_status?: Database['public']['Enums']['directive_status_enum']; - directive_status_a?: - | Database['public']['Enums']['directive_status_enum'] - | null; - directive_status_b?: - | Database['public']['Enums']['directive_status_enum'] - | null; - directive_status_c?: - | Database['public']['Enums']['directive_status_enum'] - | null; - directive_type?: Database['public']['Enums']['directive_type_enum']; - directive_watchdog_session_id?: number | null; - execution_session?: string | null; - external_reference?: string | null; - external_reference_a?: string | null; - external_reference_b?: string | null; - external_reference_c?: string | null; - id?: number; - is_on?: boolean | null; - kwh?: number | null; - kwh_credit_available?: number | null; - meter_commissioning_id?: number | null; - meter_credit_transfer_id?: number | null; - meter_id?: number | null; - meter_version?: string | null; - order_id?: number | null; - power?: number | null; - power_a?: number | null; - power_b?: number | null; - power_c?: number | null; - power_down_count?: number | null; - power_limit?: number | null; - power_limit_should_be?: number | null; - retry_of_directive_id?: number | null; - rls_organization_id?: number | null; - status_check_lock_session?: string | null; - status_last_checked_at?: string | null; - token?: string | null; - updated_at?: string | null; - voltage?: number | null; - voltage_a?: number | null; - voltage_b?: number | null; - voltage_c?: number | null; - }; - Relationships: [ - { - foreignKeyName: 'directives_rls_organization_id_fkey'; - columns: ['rls_organization_id']; - isOneToOne: false; - referencedRelation: 'organizations'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'FK_259bc4fc1c63035217ee41a719d'; - columns: ['meter_commissioning_id']; - isOneToOne: false; - referencedRelation: 'meter_commissionings'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'FK_276c89b475e9805d3322a36657a'; - columns: ['order_id']; - isOneToOne: false; - referencedRelation: 'orders'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'FK_3f3cc652d521872dbfe604b05b6'; - columns: ['meter_credit_transfer_id']; - isOneToOne: false; - referencedRelation: 'meter_credit_transfers'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'FK_637c697dc2877f08538b2fa02f5'; - columns: ['directive_watchdog_session_id']; - isOneToOne: false; - referencedRelation: 'directive_watchdog_sessions'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'FK_6c4e7feadae61cb27761734b11d'; - columns: ['meter_id']; - isOneToOne: false; - referencedRelation: 'meters'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'FK_6c4e7feadae61cb27761734b11d'; - columns: ['meter_id']; - isOneToOne: false; - referencedRelation: 'meters_with_account_and_statuses'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'FK_9064328678b805e99750773387d'; - columns: ['retry_of_directive_id']; - isOneToOne: false; - referencedRelation: 'directives'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'FK_f3a84a3f4b619b1ca8c179fff6c'; - columns: ['author_id']; - isOneToOne: false; - referencedRelation: 'accounts'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'FK_f9e3439b6ea7096d6a9d85c4503'; - columns: ['directive_batch_execution_id']; - isOneToOne: false; - referencedRelation: 'directive_batch_executions'; - referencedColumns: ['id']; - }, - ]; - }; - energy_cabins: { - Row: { - created_at: string; - grid_id: number; - id: number; - location_geom: unknown; - rls_organization_id: number | null; - }; - Insert: { - created_at?: string; - grid_id: number; - id?: number; - location_geom: unknown; - rls_organization_id?: number | null; - }; - Update: { - created_at?: string; - grid_id?: number; - id?: number; - location_geom?: unknown; - rls_organization_id?: number | null; - }; - Relationships: [ - { - foreignKeyName: 'energy_cabins_rls_organization_id_fkey'; - columns: ['rls_organization_id']; - isOneToOne: false; - referencedRelation: 'organizations'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'FK_ba81ffc1506025a549849f06f36'; - columns: ['grid_id']; - isOneToOne: false; - referencedRelation: 'grids'; - referencedColumns: ['id']; - }, - ]; - }; - features: { - Row: { - created_at: string; - id: number; - is_deleted: boolean; - uuid: string; - }; - Insert: { - created_at?: string; - id?: number; - is_deleted?: boolean; - uuid: string; - }; - Update: { - created_at?: string; - id?: number; - is_deleted?: boolean; - uuid?: string; - }; - Relationships: []; - }; - grids: { - Row: { - are_all_dcus_online: boolean; - are_all_dcus_under_high_load_threshold: boolean; - commissioned_at: string | null; - created_at: string; - current_weather: - | Database['public']['Enums']['weather_type_enum'] - | null; - default_fs_1_phase_connection_fee: number; - default_fs_3_phase_connection_fee: number; - default_hps_connection_fee: number; - deleted_at: string | null; - deployed_at: string | null; - feature_access_config: Json; - generation_external_gateway_id: string | null; - generation_external_site_id: string | null; - generation_external_system: Database['public']['Enums']['external_system_enum']; - generation_gateway_last_seen_at: string | null; - id: number; - identifier: number | null; - internal_telegram_group_chat_id: string | null; - internal_telegram_group_thread_id: string | null; - is_automatic_energy_generation_data_sync_enabled: boolean; - is_automatic_meter_energy_consumption_data_sync_enabled: boolean; - is_automatic_meter_install_enabled: boolean; - is_automatic_payout_generation_enabled: boolean; - is_cabin_meter_credit_depleting: boolean; - is_dcu_connectivity_tracking_enabled: boolean; - is_energised_notification_enabled: boolean; - is_fs_control_rule_change_notification_enabled: boolean; - is_fs_on: boolean; - is_fs_on_notification_enabled: boolean; - is_fs_on_updated_at: string | null; - is_generation_managed_by_nxt_grid: boolean; - is_hidden_from_reporting: boolean; - is_hps_on: boolean; - is_hps_on_threshold_kw: number; - is_hps_on_updated_at: string | null; - is_metering_hardware_online_notification_enabled: boolean; - is_panel_cleaning_notification_enabled: boolean; - is_router_connectivity_tracking_enabled: boolean; - is_tariff_change_notification_enabled: boolean; - is_three_phase_supported: boolean; - is_upcoming_fs_control_rule_notification_enabled: boolean; - is_using_mobile_network: boolean; - is_using_vsat: boolean; - kwh: number; - kwh_per_battery_module: number | null; - kwh_tariff: number; - kwh_tariff_essential_service: number; - kwh_tariff_full_service: number; - kwp: number; - kwp_tariff: number; - lifeline_connection_days_threshold: number; - lifeline_connection_kwh_threshold: number; - location_geom: unknown; - meter_commissioning_initial_credit_kwh: number; - meter_communication_issue_threshold_detection_days: number; - meter_consumption_issue_threshold_detection_days: number; - metering_external_system: Database['public']['Enums']['external_system_enum']; - monthly_rental: number; - name: string; - organization_id: number; - should_fs_be_on: boolean; - should_fs_be_on_updated_at: string | null; - telegram_config: Json | null; - telegram_notification_channel_invite_link: string | null; - telegram_response_path_autopilot: string | null; - telegram_response_path_token: string | null; - timezone: string; - uses_dual_meter_setup: boolean; - walkthrough_external_id: string | null; - }; - Insert: { - are_all_dcus_online?: boolean; - are_all_dcus_under_high_load_threshold?: boolean; - commissioned_at?: string | null; - created_at?: string; - current_weather?: - | Database['public']['Enums']['weather_type_enum'] - | null; - default_fs_1_phase_connection_fee?: number; - default_fs_3_phase_connection_fee?: number; - default_hps_connection_fee?: number; - deleted_at?: string | null; - deployed_at?: string | null; - feature_access_config?: Json; - generation_external_gateway_id?: string | null; - generation_external_site_id?: string | null; - generation_external_system?: Database['public']['Enums']['external_system_enum']; - generation_gateway_last_seen_at?: string | null; - id?: number; - identifier?: number | null; - internal_telegram_group_chat_id?: string | null; - internal_telegram_group_thread_id?: string | null; - is_automatic_energy_generation_data_sync_enabled?: boolean; - is_automatic_meter_energy_consumption_data_sync_enabled?: boolean; - is_automatic_meter_install_enabled?: boolean; - is_automatic_payout_generation_enabled?: boolean; - is_cabin_meter_credit_depleting?: boolean; - is_dcu_connectivity_tracking_enabled?: boolean; - is_energised_notification_enabled?: boolean; - is_fs_control_rule_change_notification_enabled?: boolean; - is_fs_on?: boolean; - is_fs_on_notification_enabled?: boolean; - is_fs_on_updated_at?: string | null; - is_generation_managed_by_nxt_grid?: boolean; - is_hidden_from_reporting?: boolean; - is_hps_on?: boolean; - is_hps_on_threshold_kw?: number; - is_hps_on_updated_at?: string | null; - is_metering_hardware_online_notification_enabled?: boolean; - is_panel_cleaning_notification_enabled?: boolean; - is_router_connectivity_tracking_enabled?: boolean; - is_tariff_change_notification_enabled?: boolean; - is_three_phase_supported?: boolean; - is_upcoming_fs_control_rule_notification_enabled?: boolean; - is_using_mobile_network?: boolean; - is_using_vsat?: boolean; - kwh?: number; - kwh_per_battery_module?: number | null; - kwh_tariff?: number; - kwh_tariff_essential_service?: number; - kwh_tariff_full_service?: number; - kwp?: number; - kwp_tariff?: number; - lifeline_connection_days_threshold?: number; - lifeline_connection_kwh_threshold?: number; - location_geom?: unknown; - meter_commissioning_initial_credit_kwh?: number; - meter_communication_issue_threshold_detection_days?: number; - meter_consumption_issue_threshold_detection_days?: number; - metering_external_system?: Database['public']['Enums']['external_system_enum']; - monthly_rental?: number; - name: string; - organization_id: number; - should_fs_be_on?: boolean; - should_fs_be_on_updated_at?: string | null; - telegram_config?: Json | null; - telegram_notification_channel_invite_link?: string | null; - telegram_response_path_autopilot?: string | null; - telegram_response_path_token?: string | null; - timezone?: string; - uses_dual_meter_setup?: boolean; - walkthrough_external_id?: string | null; - }; - Update: { - are_all_dcus_online?: boolean; - are_all_dcus_under_high_load_threshold?: boolean; - commissioned_at?: string | null; - created_at?: string; - current_weather?: - | Database['public']['Enums']['weather_type_enum'] - | null; - default_fs_1_phase_connection_fee?: number; - default_fs_3_phase_connection_fee?: number; - default_hps_connection_fee?: number; - deleted_at?: string | null; - deployed_at?: string | null; - feature_access_config?: Json; - generation_external_gateway_id?: string | null; - generation_external_site_id?: string | null; - generation_external_system?: Database['public']['Enums']['external_system_enum']; - generation_gateway_last_seen_at?: string | null; - id?: number; - identifier?: number | null; - internal_telegram_group_chat_id?: string | null; - internal_telegram_group_thread_id?: string | null; - is_automatic_energy_generation_data_sync_enabled?: boolean; - is_automatic_meter_energy_consumption_data_sync_enabled?: boolean; - is_automatic_meter_install_enabled?: boolean; - is_automatic_payout_generation_enabled?: boolean; - is_cabin_meter_credit_depleting?: boolean; - is_dcu_connectivity_tracking_enabled?: boolean; - is_energised_notification_enabled?: boolean; - is_fs_control_rule_change_notification_enabled?: boolean; - is_fs_on?: boolean; - is_fs_on_notification_enabled?: boolean; - is_fs_on_updated_at?: string | null; - is_generation_managed_by_nxt_grid?: boolean; - is_hidden_from_reporting?: boolean; - is_hps_on?: boolean; - is_hps_on_threshold_kw?: number; - is_hps_on_updated_at?: string | null; - is_metering_hardware_online_notification_enabled?: boolean; - is_panel_cleaning_notification_enabled?: boolean; - is_router_connectivity_tracking_enabled?: boolean; - is_tariff_change_notification_enabled?: boolean; - is_three_phase_supported?: boolean; - is_upcoming_fs_control_rule_notification_enabled?: boolean; - is_using_mobile_network?: boolean; - is_using_vsat?: boolean; - kwh?: number; - kwh_per_battery_module?: number | null; - kwh_tariff?: number; - kwh_tariff_essential_service?: number; - kwh_tariff_full_service?: number; - kwp?: number; - kwp_tariff?: number; - lifeline_connection_days_threshold?: number; - lifeline_connection_kwh_threshold?: number; - location_geom?: unknown; - meter_commissioning_initial_credit_kwh?: number; - meter_communication_issue_threshold_detection_days?: number; - meter_consumption_issue_threshold_detection_days?: number; - metering_external_system?: Database['public']['Enums']['external_system_enum']; - monthly_rental?: number; - name?: string; - organization_id?: number; - should_fs_be_on?: boolean; - should_fs_be_on_updated_at?: string | null; - telegram_config?: Json | null; - telegram_notification_channel_invite_link?: string | null; - telegram_response_path_autopilot?: string | null; - telegram_response_path_token?: string | null; - timezone?: string; - uses_dual_meter_setup?: boolean; - walkthrough_external_id?: string | null; + identifier?: number | null; + internal_telegram_group_chat_id?: string | null; + internal_telegram_group_thread_id?: string | null; + is_automatic_energy_generation_data_sync_enabled?: boolean; + is_automatic_meter_energy_consumption_data_sync_enabled?: boolean; + is_automatic_meter_install_enabled?: boolean; + is_cabin_meter_credit_depleting?: boolean; + is_dcu_connectivity_tracking_enabled?: boolean; + is_energised_notification_enabled?: boolean; + is_fs_control_rule_change_notification_enabled?: boolean; + is_fs_on?: boolean; + is_fs_on_notification_enabled?: boolean; + is_fs_on_updated_at?: string | null; + is_generation_managed_by_nxt_grid?: boolean; + is_hidden_from_reporting?: boolean; + is_hps_on?: boolean; + is_hps_on_threshold_kw?: number; + is_hps_on_updated_at?: string | null; + is_metering_hardware_online_notification_enabled?: boolean; + is_panel_cleaning_notification_enabled?: boolean; + is_router_connectivity_tracking_enabled?: boolean; + is_tariff_change_notification_enabled?: boolean; + is_three_phase_supported?: boolean; + is_upcoming_fs_control_rule_notification_enabled?: boolean; + is_using_mobile_network?: boolean; + is_using_vsat?: boolean; + kwh?: number; + kwh_per_battery_module?: number | null; + kwh_tariff?: number; + kwh_tariff_essential_service?: number; + kwh_tariff_full_service?: number; + kwp?: number; + kwp_tariff?: number; + lifeline_connection_days_threshold?: number; + lifeline_connection_kwh_threshold?: number; + location_geom?: unknown; + meter_commissioning_initial_credit_kwh?: number; + metering_external_system?: Database['public']['Enums']['external_system_enum']; + monthly_rental?: number; + name?: string; + organization_id?: number; + should_fs_be_on?: boolean; + should_fs_be_on_updated_at?: string | null; + telegram_config?: Json | null; + telegram_notification_channel_invite_link?: string | null; + telegram_response_path_token?: string | null; + timezone?: string; + walkthrough_external_id?: string | null; }; Relationships: [ { @@ -1479,49 +810,37 @@ export type Database = { Row: { closed_at: string | null; created_at: string; - estimated_lost_revenue: number; - external_reference: string | null; - external_system: Database['public']['Enums']['external_system_enum']; - grid_id: number | null; + external_tracking_reference: string | null; + external_tracking_system: Database['public']['Enums']['external_system_enum']; id: number; issue_status: Database['public']['Enums']['issue_status_enum']; issue_type: Database['public']['Enums']['issue_type_enum']; meter_id: number | null; - mppt_id: number | null; rls_organization_id: number | null; - snoozed_until: string | null; started_at: string | null; }; Insert: { closed_at?: string | null; created_at?: string; - estimated_lost_revenue?: number; - external_reference?: string | null; - external_system?: Database['public']['Enums']['external_system_enum']; - grid_id?: number | null; + external_tracking_reference?: string | null; + external_tracking_system?: Database['public']['Enums']['external_system_enum']; id?: number; issue_status?: Database['public']['Enums']['issue_status_enum']; issue_type: Database['public']['Enums']['issue_type_enum']; meter_id?: number | null; - mppt_id?: number | null; rls_organization_id?: number | null; - snoozed_until?: string | null; started_at?: string | null; }; Update: { closed_at?: string | null; created_at?: string; - estimated_lost_revenue?: number; - external_reference?: string | null; - external_system?: Database['public']['Enums']['external_system_enum']; - grid_id?: number | null; + external_tracking_reference?: string | null; + external_tracking_system?: Database['public']['Enums']['external_system_enum']; id?: number; issue_status?: Database['public']['Enums']['issue_status_enum']; issue_type?: Database['public']['Enums']['issue_type_enum']; meter_id?: number | null; - mppt_id?: number | null; rls_organization_id?: number | null; - snoozed_until?: string | null; started_at?: string | null; }; Relationships: [ @@ -1539,20 +858,6 @@ export type Database = { referencedRelation: 'meters_with_account_and_statuses'; referencedColumns: ['id']; }, - { - foreignKeyName: 'FK_be8d64bd0c97a739ac323dde9dd'; - columns: ['grid_id']; - isOneToOne: false; - referencedRelation: 'grids'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'FK_bf46db804a794d1d09eb050ac77'; - columns: ['mppt_id']; - isOneToOne: false; - referencedRelation: 'mppts'; - referencedColumns: ['id']; - }, { foreignKeyName: 'issues_rls_organization_id_fkey'; columns: ['rls_organization_id']; @@ -1562,226 +867,6 @@ export type Database = { }, ]; }; - lorawan_directives: { - Row: { - author_id: number | null; - can_be_retried: boolean | null; - created_at: string; - current_a: number | null; - current_b: number | null; - current_c: number | null; - directive_batch_execution_id: number | null; - directive_direction: Database['public']['Enums']['directive_direction_enum']; - directive_error: - | Database['public']['Enums']['directive_error_enum'] - | null; - directive_phase: - | Database['public']['Enums']['directive_phase_enum'] - | null; - directive_status: Database['public']['Enums']['directive_status_enum']; - directive_type: - | Database['public']['Enums']['directive_type_enum'] - | null; - directive_watchdog_session_id: number | null; - external_reference: string | null; - id: number; - is_on: boolean | null; - kwh: number | null; - kwh_credit_available: number | null; - meter_commissioning_id: number | null; - meter_id: number | null; - order_id: number | null; - power_a: number | null; - power_b: number | null; - power_c: number | null; - power_limit: number | null; - power_limit_should_be: number | null; - retry_of_directive_id: number | null; - rls_organization_id: number | null; - token: string | null; - value: Json | null; - voltage_a: number | null; - voltage_b: number | null; - voltage_c: number | null; - }; - Insert: { - author_id?: number | null; - can_be_retried?: boolean | null; - created_at?: string; - current_a?: number | null; - current_b?: number | null; - current_c?: number | null; - directive_batch_execution_id?: number | null; - directive_direction: Database['public']['Enums']['directive_direction_enum']; - directive_error?: - | Database['public']['Enums']['directive_error_enum'] - | null; - directive_phase?: - | Database['public']['Enums']['directive_phase_enum'] - | null; - directive_status: Database['public']['Enums']['directive_status_enum']; - directive_type?: - | Database['public']['Enums']['directive_type_enum'] - | null; - directive_watchdog_session_id?: number | null; - external_reference?: string | null; - id?: number; - is_on?: boolean | null; - kwh?: number | null; - kwh_credit_available?: number | null; - meter_commissioning_id?: number | null; - meter_id?: number | null; - order_id?: number | null; - power_a?: number | null; - power_b?: number | null; - power_c?: number | null; - power_limit?: number | null; - power_limit_should_be?: number | null; - retry_of_directive_id?: number | null; - rls_organization_id?: number | null; - token?: string | null; - value?: Json | null; - voltage_a?: number | null; - voltage_b?: number | null; - voltage_c?: number | null; - }; - Update: { - author_id?: number | null; - can_be_retried?: boolean | null; - created_at?: string; - current_a?: number | null; - current_b?: number | null; - current_c?: number | null; - directive_batch_execution_id?: number | null; - directive_direction?: Database['public']['Enums']['directive_direction_enum']; - directive_error?: - | Database['public']['Enums']['directive_error_enum'] - | null; - directive_phase?: - | Database['public']['Enums']['directive_phase_enum'] - | null; - directive_status?: Database['public']['Enums']['directive_status_enum']; - directive_type?: - | Database['public']['Enums']['directive_type_enum'] - | null; - directive_watchdog_session_id?: number | null; - external_reference?: string | null; - id?: number; - is_on?: boolean | null; - kwh?: number | null; - kwh_credit_available?: number | null; - meter_commissioning_id?: number | null; - meter_id?: number | null; - order_id?: number | null; - power_a?: number | null; - power_b?: number | null; - power_c?: number | null; - power_limit?: number | null; - power_limit_should_be?: number | null; - retry_of_directive_id?: number | null; - rls_organization_id?: number | null; - token?: string | null; - value?: Json | null; - voltage_a?: number | null; - voltage_b?: number | null; - voltage_c?: number | null; - }; - Relationships: [ - { - foreignKeyName: 'lorawan_directives_author_id_fkey'; - columns: ['author_id']; - isOneToOne: false; - referencedRelation: 'accounts'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'lorawan_directives_directive_batch_execution_id_fkey'; - columns: ['directive_batch_execution_id']; - isOneToOne: false; - referencedRelation: 'directive_batch_executions'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'lorawan_directives_directive_watchdog_session_id_fkey'; - columns: ['directive_watchdog_session_id']; - isOneToOne: false; - referencedRelation: 'directive_watchdog_sessions'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'lorawan_directives_meter_commissioning_id_fkey'; - columns: ['meter_commissioning_id']; - isOneToOne: false; - referencedRelation: 'meter_commissionings'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'lorawan_directives_meter_id_fkey'; - columns: ['meter_id']; - isOneToOne: false; - referencedRelation: 'meters'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'lorawan_directives_meter_id_fkey'; - columns: ['meter_id']; - isOneToOne: false; - referencedRelation: 'meters_with_account_and_statuses'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'lorawan_directives_order_id_fkey'; - columns: ['order_id']; - isOneToOne: false; - referencedRelation: 'orders'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'lorawan_directives_retry_of_directive_id_fkey'; - columns: ['retry_of_directive_id']; - isOneToOne: false; - referencedRelation: 'lorawan_directives'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'lorawan_directives_rls_organization_id_fkey'; - columns: ['rls_organization_id']; - isOneToOne: false; - referencedRelation: 'organizations'; - referencedColumns: ['id']; - }, - ]; - }; - member_feature: { - Row: { - feature_id: number; - member_id: number; - }; - Insert: { - feature_id: number; - member_id: number; - }; - Update: { - feature_id?: number; - member_id?: number; - }; - Relationships: [ - { - foreignKeyName: 'FK_4da956d70d1de38087b251b9978'; - columns: ['feature_id']; - isOneToOne: false; - referencedRelation: 'features'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'FK_c312f698867204bf4f1fd149c91'; - columns: ['member_id']; - isOneToOne: false; - referencedRelation: 'members'; - referencedColumns: ['id']; - }, - ]; - }; members: { Row: { account_id: number | null; @@ -1840,59 +925,59 @@ export type Database = { }, ]; }; - meter_commissionings: { + meter_command_batch_executions: { Row: { + completed_at: string | null; created_at: string; - failed_steps: number | null; + failed_count: number; id: number; - initialised_steps: number | null; - lock_session: string | null; - meter_commissioning_status: Database['public']['Enums']['meter_commissioning_status_enum']; - metering_hardware_install_session_id: number | null; - pending_steps: number | null; - processing_steps: number | null; + meter_command_batch_id: number; + pending_count: number; + processed_count: number; + processing_count: number; + qualified_at: string | null; rls_organization_id: number | null; - successful_steps: number | null; - total_steps: number | null; + successful_count: number; + total_count: number; }; Insert: { + completed_at?: string | null; created_at?: string; - failed_steps?: number | null; + failed_count?: number; id?: number; - initialised_steps?: number | null; - lock_session?: string | null; - meter_commissioning_status?: Database['public']['Enums']['meter_commissioning_status_enum']; - metering_hardware_install_session_id?: number | null; - pending_steps?: number | null; - processing_steps?: number | null; + meter_command_batch_id: number; + pending_count?: number; + processed_count?: number; + processing_count?: number; + qualified_at?: string | null; rls_organization_id?: number | null; - successful_steps?: number | null; - total_steps?: number | null; + successful_count?: number; + total_count?: number; }; Update: { + completed_at?: string | null; created_at?: string; - failed_steps?: number | null; + failed_count?: number; id?: number; - initialised_steps?: number | null; - lock_session?: string | null; - meter_commissioning_status?: Database['public']['Enums']['meter_commissioning_status_enum']; - metering_hardware_install_session_id?: number | null; - pending_steps?: number | null; - processing_steps?: number | null; + meter_command_batch_id?: number; + pending_count?: number; + processed_count?: number; + processing_count?: number; + qualified_at?: string | null; rls_organization_id?: number | null; - successful_steps?: number | null; - total_steps?: number | null; + successful_count?: number; + total_count?: number; }; Relationships: [ { - foreignKeyName: 'FK_6684f0790c54ed3d441f78df91a'; - columns: ['metering_hardware_install_session_id']; + foreignKeyName: 'FK_7b0737005e7c358392d87ebb329'; + columns: ['meter_command_batch_id']; isOneToOne: false; - referencedRelation: 'metering_hardware_install_sessions'; + referencedRelation: 'meter_command_batches'; referencedColumns: ['id']; }, { - foreignKeyName: 'meter_commissionings_rls_organization_id_fkey'; + foreignKeyName: 'meter_command_batch_executions_rls_organization_id_fkey'; columns: ['rls_organization_id']; isOneToOne: false; referencedRelation: 'organizations'; @@ -1900,84 +985,111 @@ export type Database = { }, ]; }; - meter_credit_transfers: { + meter_command_batches: { Row: { - amount: number; author_id: number | null; created_at: string; - currency: Database['public']['Enums']['currency_enum']; + fs_command: + Database['public']['Enums']['fs_command_type_enum'] | null; + grid_id: number | null; + hour: number; id: number; - lock_session: string | null; - meter_credit_transfer_status: Database['public']['Enums']['meter_credit_transfer_status_enum']; - receiver_meter_id: number; + is_deleted: boolean; + is_repeating: boolean; + minute: number; rls_organization_id: number | null; - sender_meter_id: number; - sender_meter_set_to_amount: number | null; + task_type: + Database['public']['Enums']['meter_interaction_type_enum'] | null; + updated_at: string | null; }; Insert: { - amount: number; author_id?: number | null; created_at?: string; - currency: Database['public']['Enums']['currency_enum']; + fs_command?: + Database['public']['Enums']['fs_command_type_enum'] | null; + grid_id?: number | null; + hour: number; id?: number; - lock_session?: string | null; - meter_credit_transfer_status?: Database['public']['Enums']['meter_credit_transfer_status_enum']; - receiver_meter_id: number; + is_deleted?: boolean; + is_repeating?: boolean; + minute: number; rls_organization_id?: number | null; - sender_meter_id: number; - sender_meter_set_to_amount?: number | null; + task_type?: + Database['public']['Enums']['meter_interaction_type_enum'] | null; + updated_at?: string | null; }; Update: { - amount?: number; author_id?: number | null; created_at?: string; - currency?: Database['public']['Enums']['currency_enum']; + fs_command?: + Database['public']['Enums']['fs_command_type_enum'] | null; + grid_id?: number | null; + hour?: number; id?: number; - lock_session?: string | null; - meter_credit_transfer_status?: Database['public']['Enums']['meter_credit_transfer_status_enum']; - receiver_meter_id?: number; + is_deleted?: boolean; + is_repeating?: boolean; + minute?: number; rls_organization_id?: number | null; - sender_meter_id?: number; - sender_meter_set_to_amount?: number | null; + task_type?: + Database['public']['Enums']['meter_interaction_type_enum'] | null; + updated_at?: string | null; }; Relationships: [ { - foreignKeyName: 'FK_3b4ebdcd826c46e4b5c165a0840'; - columns: ['sender_meter_id']; - isOneToOne: false; - referencedRelation: 'meters'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'FK_3b4ebdcd826c46e4b5c165a0840'; - columns: ['sender_meter_id']; + foreignKeyName: 'FK_5c44fae05dc443720f62664e563'; + columns: ['grid_id']; isOneToOne: false; - referencedRelation: 'meters_with_account_and_statuses'; + referencedRelation: 'grids'; referencedColumns: ['id']; }, { - foreignKeyName: 'FK_b196e2b2b291716e18a11611350'; + foreignKeyName: 'FK_6e26cb6e966f484f0f897127c84'; columns: ['author_id']; isOneToOne: false; referencedRelation: 'accounts'; referencedColumns: ['id']; }, { - foreignKeyName: 'FK_e83e8905a777a669de1c2391b77'; - columns: ['receiver_meter_id']; + foreignKeyName: 'meter_command_batches_rls_organization_id_fkey'; + columns: ['rls_organization_id']; isOneToOne: false; - referencedRelation: 'meters'; + referencedRelation: 'organizations'; referencedColumns: ['id']; }, + ]; + }; + meter_commissionings: { + Row: { + created_at: string; + id: number; + meter_commissioning_status: Database['public']['Enums']['meter_commissioning_status_enum']; + metering_hardware_install_session_id: number | null; + rls_organization_id: number | null; + }; + Insert: { + created_at?: string; + id?: number; + meter_commissioning_status?: Database['public']['Enums']['meter_commissioning_status_enum']; + metering_hardware_install_session_id?: number | null; + rls_organization_id?: number | null; + }; + Update: { + created_at?: string; + id?: number; + meter_commissioning_status?: Database['public']['Enums']['meter_commissioning_status_enum']; + metering_hardware_install_session_id?: number | null; + rls_organization_id?: number | null; + }; + Relationships: [ { - foreignKeyName: 'FK_e83e8905a777a669de1c2391b77'; - columns: ['receiver_meter_id']; + foreignKeyName: 'FK_6684f0790c54ed3d441f78df91a'; + columns: ['metering_hardware_install_session_id']; isOneToOne: false; - referencedRelation: 'meters_with_account_and_statuses'; + referencedRelation: 'metering_hardware_install_sessions'; referencedColumns: ['id']; }, { - foreignKeyName: 'meter_credit_transfers_rls_organization_id_fkey'; + foreignKeyName: 'meter_commissionings_rls_organization_id_fkey'; columns: ['rls_organization_id']; isOneToOne: false; referencedRelation: 'organizations'; @@ -2042,7 +1154,7 @@ export type Database = { foreignKeyName: 'meter_interactions_batch_execution_id_fkey'; columns: ['batch_execution_id']; isOneToOne: false; - referencedRelation: 'directive_batch_executions'; + referencedRelation: 'meter_command_batch_executions'; referencedColumns: ['id']; }, { @@ -2208,19 +1320,14 @@ export type Database = { balance: number | null; balance_updated_at: string | null; communication_protocol: - | Database['public']['Enums']['communication_protocol_enum'] - | null; + Database['public']['Enums']['communication_protocol_enum'] | null; connection_id: number | null; connection_metrics: Json | null; coord_accuracy: number; created_at: string; - current_special_status: - | Database['public']['Enums']['directive_special_status_enum'] - | null; dcu_id: number | null; decoder_key: string | null; deleted_at: string | null; - device_id: number | null; external_reference: string; external_system: Database['public']['Enums']['external_system_enum']; id: number; @@ -2229,7 +1336,6 @@ export type Database = { is_manual_mode_on_updated_at: string | null; is_on: boolean | null; is_on_updated_at: string | null; - is_simulated: boolean; is_starred: boolean; is_test_mode_on: boolean; issue_check_execution_session: string | null; @@ -2249,16 +1355,12 @@ export type Database = { nickname: string | null; pole_id: number | null; power: number | null; - power_down_count: number | null; - power_down_count_updated_at: string | null; power_limit: number | null; power_limit_hps_mode: number; power_limit_should_be: number | null; power_limit_should_be_updated_at: string | null; power_limit_updated_at: string | null; power_updated_at: string | null; - pulse_counter_kwh: number | null; - pulse_counter_kwh_updated_at: string | null; rls_grid_id: number | null; rls_organization_id: number | null; should_be_on: boolean | null; @@ -2266,26 +1368,19 @@ export type Database = { version: string | null; voltage: number | null; voltage_updated_at: string | null; - watchdog_last_run_at: string | null; - watchdog_session: string | null; }; Insert: { balance?: number | null; balance_updated_at?: string | null; communication_protocol?: - | Database['public']['Enums']['communication_protocol_enum'] - | null; + Database['public']['Enums']['communication_protocol_enum'] | null; connection_id?: number | null; connection_metrics?: Json | null; coord_accuracy?: number; created_at?: string; - current_special_status?: - | Database['public']['Enums']['directive_special_status_enum'] - | null; dcu_id?: number | null; decoder_key?: string | null; deleted_at?: string | null; - device_id?: number | null; external_reference: string; external_system: Database['public']['Enums']['external_system_enum']; id?: number; @@ -2294,7 +1389,6 @@ export type Database = { is_manual_mode_on_updated_at?: string | null; is_on?: boolean | null; is_on_updated_at?: string | null; - is_simulated?: boolean; is_starred?: boolean; is_test_mode_on?: boolean; issue_check_execution_session?: string | null; @@ -2314,16 +1408,12 @@ export type Database = { nickname?: string | null; pole_id?: number | null; power?: number | null; - power_down_count?: number | null; - power_down_count_updated_at?: string | null; power_limit?: number | null; power_limit_hps_mode?: number; power_limit_should_be?: number | null; power_limit_should_be_updated_at?: string | null; power_limit_updated_at?: string | null; power_updated_at?: string | null; - pulse_counter_kwh?: number | null; - pulse_counter_kwh_updated_at?: string | null; rls_grid_id?: number | null; rls_organization_id?: number | null; should_be_on?: boolean | null; @@ -2331,26 +1421,19 @@ export type Database = { version?: string | null; voltage?: number | null; voltage_updated_at?: string | null; - watchdog_last_run_at?: string | null; - watchdog_session?: string | null; }; Update: { balance?: number | null; balance_updated_at?: string | null; communication_protocol?: - | Database['public']['Enums']['communication_protocol_enum'] - | null; + Database['public']['Enums']['communication_protocol_enum'] | null; connection_id?: number | null; connection_metrics?: Json | null; coord_accuracy?: number; created_at?: string; - current_special_status?: - | Database['public']['Enums']['directive_special_status_enum'] - | null; dcu_id?: number | null; decoder_key?: string | null; deleted_at?: string | null; - device_id?: number | null; external_reference?: string; external_system?: Database['public']['Enums']['external_system_enum']; id?: number; @@ -2359,7 +1442,6 @@ export type Database = { is_manual_mode_on_updated_at?: string | null; is_on?: boolean | null; is_on_updated_at?: string | null; - is_simulated?: boolean; is_starred?: boolean; is_test_mode_on?: boolean; issue_check_execution_session?: string | null; @@ -2379,16 +1461,12 @@ export type Database = { nickname?: string | null; pole_id?: number | null; power?: number | null; - power_down_count?: number | null; - power_down_count_updated_at?: string | null; power_limit?: number | null; power_limit_hps_mode?: number; power_limit_should_be?: number | null; power_limit_should_be_updated_at?: string | null; power_limit_updated_at?: string | null; power_updated_at?: string | null; - pulse_counter_kwh?: number | null; - pulse_counter_kwh_updated_at?: string | null; rls_grid_id?: number | null; rls_organization_id?: number | null; should_be_on?: boolean | null; @@ -2396,8 +1474,6 @@ export type Database = { version?: string | null; voltage?: number | null; voltage_updated_at?: string | null; - watchdog_last_run_at?: string | null; - watchdog_session?: string | null; }; Relationships: [ { @@ -2435,13 +1511,6 @@ export type Database = { referencedRelation: 'connections'; referencedColumns: ['id']; }, - { - foreignKeyName: 'meters_device_id_fkey'; - columns: ['device_id']; - isOneToOne: true; - referencedRelation: 'devices'; - referencedColumns: ['id']; - }, { foreignKeyName: 'meters_rls_grid_id_fkey'; columns: ['rls_grid_id']; @@ -2458,24 +1527,6 @@ export type Database = { }, ]; }; - migrations: { - Row: { - id: number; - name: string; - timestamp: number; - }; - Insert: { - id?: number; - name: string; - timestamp: number; - }; - Update: { - id?: number; - name?: string; - timestamp?: number; - }; - Relationships: []; - }; mppts: { Row: { azimuth: number | null; @@ -2644,12 +1695,10 @@ export type Database = { Row: { account_id: number | null; carrier_external_system: - | Database['public']['Enums']['external_system_enum'] - | null; + Database['public']['Enums']['external_system_enum'] | null; chat_id: string | null; connector_external_system: - | Database['public']['Enums']['external_system_enum'] - | null; + Database['public']['Enums']['external_system_enum'] | null; created_at: string; email: string | null; external_reference: string | null; @@ -2668,12 +1717,10 @@ export type Database = { Insert: { account_id?: number | null; carrier_external_system?: - | Database['public']['Enums']['external_system_enum'] - | null; + Database['public']['Enums']['external_system_enum'] | null; chat_id?: string | null; connector_external_system?: - | Database['public']['Enums']['external_system_enum'] - | null; + Database['public']['Enums']['external_system_enum'] | null; created_at?: string; email?: string | null; external_reference?: string | null; @@ -2692,12 +1739,10 @@ export type Database = { Update: { account_id?: number | null; carrier_external_system?: - | Database['public']['Enums']['external_system_enum'] - | null; + Database['public']['Enums']['external_system_enum'] | null; chat_id?: string | null; connector_external_system?: - | Database['public']['Enums']['external_system_enum'] - | null; + Database['public']['Enums']['external_system_enum'] | null; created_at?: string; email?: string | null; external_reference?: string | null; @@ -2750,45 +1795,33 @@ export type Database = { author_id: number | null; created_at: string; currency: Database['public']['Enums']['currency_enum']; - directive_id: number | null; external_reference: string | null; - external_system: - | Database['public']['Enums']['external_system_enum'] - | null; historical_grid_id: number | null; id: number; lock_session: string | null; - lorawan_directive_id: number | null; meta_author_id: number | null; meta_author_name: string | null; meta_author_type: - | Database['public']['Enums']['account_type_enum'] - | null; + Database['public']['Enums']['account_type_enum'] | null; meta_is_hidden_from_reporting: boolean | null; meta_order_type: - | Database['public']['Enums']['order_type_enum'] - | null; + Database['public']['Enums']['order_type_enum'] | null; meta_receiver_id: number | null; meta_receiver_id_part_2: number | null; meta_receiver_name: string | null; meta_receiver_name_part_2: string | null; meta_receiver_type: - | Database['public']['Enums']['order_actor_type_enum'] - | null; + Database['public']['Enums']['order_actor_type_enum'] | null; meta_sender_id: number | null; meta_sender_name: string | null; meta_sender_name_part_2: string | null; meta_sender_type: - | Database['public']['Enums']['order_actor_type_enum'] - | null; - meter_credit_transfer_id: number | null; + Database['public']['Enums']['order_actor_type_enum'] | null; order_status: Database['public']['Enums']['order_status_enum']; payment_channel: - | Database['public']['Enums']['payment_channel_enum'] - | null; + Database['public']['Enums']['payment_channel_enum'] | null; payment_method: - | Database['public']['Enums']['payment_method_enum'] - | null; + Database['public']['Enums']['payment_method_enum'] | null; receiver_wallet_id: number | null; rls_organization_id: number | null; sender_wallet_id: number | null; @@ -2802,45 +1835,33 @@ export type Database = { author_id?: number | null; created_at?: string; currency: Database['public']['Enums']['currency_enum']; - directive_id?: number | null; external_reference?: string | null; - external_system?: - | Database['public']['Enums']['external_system_enum'] - | null; historical_grid_id?: number | null; id?: number; lock_session?: string | null; - lorawan_directive_id?: number | null; meta_author_id?: number | null; meta_author_name?: string | null; meta_author_type?: - | Database['public']['Enums']['account_type_enum'] - | null; + Database['public']['Enums']['account_type_enum'] | null; meta_is_hidden_from_reporting?: boolean | null; meta_order_type?: - | Database['public']['Enums']['order_type_enum'] - | null; + Database['public']['Enums']['order_type_enum'] | null; meta_receiver_id?: number | null; meta_receiver_id_part_2?: number | null; meta_receiver_name?: string | null; meta_receiver_name_part_2?: string | null; meta_receiver_type?: - | Database['public']['Enums']['order_actor_type_enum'] - | null; + Database['public']['Enums']['order_actor_type_enum'] | null; meta_sender_id?: number | null; meta_sender_name?: string | null; meta_sender_name_part_2?: string | null; meta_sender_type?: - | Database['public']['Enums']['order_actor_type_enum'] - | null; - meter_credit_transfer_id?: number | null; + Database['public']['Enums']['order_actor_type_enum'] | null; order_status?: Database['public']['Enums']['order_status_enum']; payment_channel?: - | Database['public']['Enums']['payment_channel_enum'] - | null; + Database['public']['Enums']['payment_channel_enum'] | null; payment_method?: - | Database['public']['Enums']['payment_method_enum'] - | null; + Database['public']['Enums']['payment_method_enum'] | null; receiver_wallet_id?: number | null; rls_organization_id?: number | null; sender_wallet_id?: number | null; @@ -2854,45 +1875,33 @@ export type Database = { author_id?: number | null; created_at?: string; currency?: Database['public']['Enums']['currency_enum']; - directive_id?: number | null; external_reference?: string | null; - external_system?: - | Database['public']['Enums']['external_system_enum'] - | null; historical_grid_id?: number | null; id?: number; lock_session?: string | null; - lorawan_directive_id?: number | null; meta_author_id?: number | null; meta_author_name?: string | null; meta_author_type?: - | Database['public']['Enums']['account_type_enum'] - | null; + Database['public']['Enums']['account_type_enum'] | null; meta_is_hidden_from_reporting?: boolean | null; meta_order_type?: - | Database['public']['Enums']['order_type_enum'] - | null; + Database['public']['Enums']['order_type_enum'] | null; meta_receiver_id?: number | null; meta_receiver_id_part_2?: number | null; meta_receiver_name?: string | null; meta_receiver_name_part_2?: string | null; meta_receiver_type?: - | Database['public']['Enums']['order_actor_type_enum'] - | null; + Database['public']['Enums']['order_actor_type_enum'] | null; meta_sender_id?: number | null; meta_sender_name?: string | null; meta_sender_name_part_2?: string | null; meta_sender_type?: - | Database['public']['Enums']['order_actor_type_enum'] - | null; - meter_credit_transfer_id?: number | null; + Database['public']['Enums']['order_actor_type_enum'] | null; order_status?: Database['public']['Enums']['order_status_enum']; payment_channel?: - | Database['public']['Enums']['payment_channel_enum'] - | null; + Database['public']['Enums']['payment_channel_enum'] | null; payment_method?: - | Database['public']['Enums']['payment_method_enum'] - | null; + Database['public']['Enums']['payment_method_enum'] | null; receiver_wallet_id?: number | null; rls_organization_id?: number | null; sender_wallet_id?: number | null; @@ -2902,13 +1911,6 @@ export type Database = { ussd_session_id?: number | null; }; Relationships: [ - { - foreignKeyName: 'FK_093ca3525311b8a12f6cf6b1c9b'; - columns: ['directive_id']; - isOneToOne: true; - referencedRelation: 'directives'; - referencedColumns: ['id']; - }, { foreignKeyName: 'FK_0b03d1bc1cff784570333129e63'; columns: ['historical_grid_id']; @@ -2931,502 +1933,70 @@ export type Database = { referencedColumns: ['id']; }, { - foreignKeyName: 'FK_8f12fe9c7b078122adcae80375d'; - columns: ['sender_wallet_id']; - isOneToOne: false; - referencedRelation: 'wallets'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'FK_92081072063eaf51300ab6c267d'; - columns: ['ussd_session_id']; - isOneToOne: true; - referencedRelation: 'ussd_sessions'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'FK_a53c58bdb5ae0193f17497c81b4'; - columns: ['meter_credit_transfer_id']; - isOneToOne: true; - referencedRelation: 'meter_credit_transfers'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'orders_lorawan_directive_id_fkey'; - columns: ['lorawan_directive_id']; - isOneToOne: false; - referencedRelation: 'lorawan_directives'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'orders_rls_organization_id_fkey'; - columns: ['rls_organization_id']; - isOneToOne: false; - referencedRelation: 'organizations'; - referencedColumns: ['id']; - }, - ]; - }; - organizations: { - Row: { - address: string | null; - created_at: string; - deleted_at: string | null; - developer_group_telegram_chat_id: string | null; - email: string | null; - epicollect_contract_last_sync_at: string | null; - epicollect_contract_survey_client_id: string | null; - epicollect_contract_survey_secret: string | null; - epicollect_contract_survey_slug: string | null; - formal_name: string | null; - id: number; - name: string; - organization_type: Database['public']['Enums']['organization_type_enum']; - pd_hero_google_drive_folder_id: string | null; - phone: string | null; - }; - Insert: { - address?: string | null; - created_at?: string; - deleted_at?: string | null; - developer_group_telegram_chat_id?: string | null; - email?: string | null; - epicollect_contract_last_sync_at?: string | null; - epicollect_contract_survey_client_id?: string | null; - epicollect_contract_survey_secret?: string | null; - epicollect_contract_survey_slug?: string | null; - formal_name?: string | null; - id?: number; - name: string; - organization_type?: Database['public']['Enums']['organization_type_enum']; - pd_hero_google_drive_folder_id?: string | null; - phone?: string | null; - }; - Update: { - address?: string | null; - created_at?: string; - deleted_at?: string | null; - developer_group_telegram_chat_id?: string | null; - email?: string | null; - epicollect_contract_last_sync_at?: string | null; - epicollect_contract_survey_client_id?: string | null; - epicollect_contract_survey_secret?: string | null; - epicollect_contract_survey_slug?: string | null; - formal_name?: string | null; - id?: number; - name?: string; - organization_type?: Database['public']['Enums']['organization_type_enum']; - pd_hero_google_drive_folder_id?: string | null; - phone?: string | null; - }; - Relationships: []; - }; - payouts: { - Row: { - approved_amount: number | null; - approved_by_account_id: number | null; - bank_account_id: number | null; - created_at: string; - details: Json | null; - draft_link: string | null; - ended_at: string; - external_reference: string | null; - external_system: - | Database['public']['Enums']['external_system_enum'] - | null; - grid_id: number; - id: number; - payout_status: Database['public']['Enums']['payout_status_enum']; - proposed_amount: number; - started_at: string; - }; - Insert: { - approved_amount?: number | null; - approved_by_account_id?: number | null; - bank_account_id?: number | null; - created_at?: string; - details?: Json | null; - draft_link?: string | null; - ended_at: string; - external_reference?: string | null; - external_system?: - | Database['public']['Enums']['external_system_enum'] - | null; - grid_id: number; - id?: number; - payout_status: Database['public']['Enums']['payout_status_enum']; - proposed_amount: number; - started_at: string; - }; - Update: { - approved_amount?: number | null; - approved_by_account_id?: number | null; - bank_account_id?: number | null; - created_at?: string; - details?: Json | null; - draft_link?: string | null; - ended_at?: string; - external_reference?: string | null; - external_system?: - | Database['public']['Enums']['external_system_enum'] - | null; - grid_id?: number; - id?: number; - payout_status?: Database['public']['Enums']['payout_status_enum']; - proposed_amount?: number; - started_at?: string; - }; - Relationships: [ - { - foreignKeyName: 'FK_7796c1df57eeefa9173325627c4'; - columns: ['bank_account_id']; - isOneToOne: false; - referencedRelation: 'bank_accounts'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'FK_e2e6de2a3c003b36f840829079a'; - columns: ['approved_by_account_id']; - isOneToOne: false; - referencedRelation: 'accounts'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'FK_e8bc1a446b4df957b8d37af1cd0'; - columns: ['grid_id']; - isOneToOne: false; - referencedRelation: 'grids'; - referencedColumns: ['id']; - }, - ]; - }; - pd_action_templates: { - Row: { - created_at: string; - depends_on: Json | null; - id: number; - key: string | null; - name: string; - parameters: Json; - pd_action_type: Database['public']['Enums']['pd_action_type_enum']; - pd_document_template_id: number | null; - pd_flow_template_id: number; - }; - Insert: { - created_at?: string; - depends_on?: Json | null; - id?: number; - key?: string | null; - name: string; - parameters?: Json; - pd_action_type: Database['public']['Enums']['pd_action_type_enum']; - pd_document_template_id?: number | null; - pd_flow_template_id: number; - }; - Update: { - created_at?: string; - depends_on?: Json | null; - id?: number; - key?: string | null; - name?: string; - parameters?: Json; - pd_action_type?: Database['public']['Enums']['pd_action_type_enum']; - pd_document_template_id?: number | null; - pd_flow_template_id?: number; - }; - Relationships: [ - { - foreignKeyName: 'pd_action_templates_pd_document_template_id_fkey'; - columns: ['pd_document_template_id']; - isOneToOne: false; - referencedRelation: 'pd_document_templates'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'pd_action_templates_pd_flow_template_id_fkey'; - columns: ['pd_flow_template_id']; - isOneToOne: false; - referencedRelation: 'pd_flow_templates'; - referencedColumns: ['id']; - }, - ]; - }; - pd_actions: { - Row: { - created_at: string; - depends_on: Json | null; - id: number; - key: string | null; - latest_pd_document_id: number | null; - name: string; - parameters: Json; - pd_action_status: Database['public']['Enums']['pd_action_status_enum']; - pd_action_template_id: number | null; - pd_action_type: Database['public']['Enums']['pd_action_type_enum']; - pd_flow_id: number | null; - pd_section_id: number | null; - }; - Insert: { - created_at?: string; - depends_on?: Json | null; - id?: number; - key?: string | null; - latest_pd_document_id?: number | null; - name: string; - parameters?: Json; - pd_action_status?: Database['public']['Enums']['pd_action_status_enum']; - pd_action_template_id?: number | null; - pd_action_type: Database['public']['Enums']['pd_action_type_enum']; - pd_flow_id?: number | null; - pd_section_id?: number | null; - }; - Update: { - created_at?: string; - depends_on?: Json | null; - id?: number; - key?: string | null; - latest_pd_document_id?: number | null; - name?: string; - parameters?: Json; - pd_action_status?: Database['public']['Enums']['pd_action_status_enum']; - pd_action_template_id?: number | null; - pd_action_type?: Database['public']['Enums']['pd_action_type_enum']; - pd_flow_id?: number | null; - pd_section_id?: number | null; - }; - Relationships: [ - { - foreignKeyName: 'pd_actions_latest_pd_document_id_fkey'; - columns: ['latest_pd_document_id']; - isOneToOne: false; - referencedRelation: 'pd_documents'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'pd_actions_pd_action_template_id_fkey'; - columns: ['pd_action_template_id']; - isOneToOne: false; - referencedRelation: 'pd_action_templates'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'pd_actions_pd_flow_id_fkey'; - columns: ['pd_flow_id']; - isOneToOne: false; - referencedRelation: 'pd_flows'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'pd_actions_pd_section_id_fkey'; - columns: ['pd_section_id']; - isOneToOne: false; - referencedRelation: 'pd_actions'; - referencedColumns: ['id']; - }, - ]; - }; - pd_audits: { - Row: { - author_id: number | null; - created_at: string; - id: number; - message: string | null; - pd_action_id: number | null; - }; - Insert: { - author_id?: number | null; - created_at?: string; - id?: number; - message?: string | null; - pd_action_id?: number | null; - }; - Update: { - author_id?: number | null; - created_at?: string; - id?: number; - message?: string | null; - pd_action_id?: number | null; - }; - Relationships: [ - { - foreignKeyName: 'pd_audits_author_id_fkey'; - columns: ['author_id']; - isOneToOne: false; - referencedRelation: 'accounts'; - referencedColumns: ['id']; - }, - { - foreignKeyName: 'pd_audits_pd_action_id_fkey'; - columns: ['pd_action_id']; + foreignKeyName: 'FK_8f12fe9c7b078122adcae80375d'; + columns: ['sender_wallet_id']; isOneToOne: false; - referencedRelation: 'pd_actions'; + referencedRelation: 'wallets'; referencedColumns: ['id']; }, - ]; - }; - pd_document_templates: { - Row: { - created_at: string; - google_drive_template_id: string; - id: number; - parameters: Json; - pd_document_type: - | Database['public']['Enums']['pd_document_type_enum'] - | null; - title: string | null; - }; - Insert: { - created_at?: string; - google_drive_template_id: string; - id?: number; - parameters?: Json; - pd_document_type?: - | Database['public']['Enums']['pd_document_type_enum'] - | null; - title?: string | null; - }; - Update: { - created_at?: string; - google_drive_template_id?: string; - id?: number; - parameters?: Json; - pd_document_type?: - | Database['public']['Enums']['pd_document_type_enum'] - | null; - title?: string | null; - }; - Relationships: []; - }; - pd_documents: { - Row: { - created_at: string; - google_drive_document_id: string; - id: number; - pd_action_id: number | null; - pd_document_type: - | Database['public']['Enums']['pd_document_type_enum'] - | null; - title: string | null; - }; - Insert: { - created_at?: string; - google_drive_document_id: string; - id?: number; - pd_action_id?: number | null; - pd_document_type?: - | Database['public']['Enums']['pd_document_type_enum'] - | null; - title?: string | null; - }; - Update: { - created_at?: string; - google_drive_document_id?: string; - id?: number; - pd_action_id?: number | null; - pd_document_type?: - | Database['public']['Enums']['pd_document_type_enum'] - | null; - title?: string | null; - }; - Relationships: [ { - foreignKeyName: 'pd_documents_pd_action_id_fkey'; - columns: ['pd_action_id']; - isOneToOne: false; - referencedRelation: 'pd_actions'; + foreignKeyName: 'FK_92081072063eaf51300ab6c267d'; + columns: ['ussd_session_id']; + isOneToOne: true; + referencedRelation: 'ussd_sessions'; referencedColumns: ['id']; }, - ]; - }; - pd_flow_templates: { - Row: { - created_at: string; - id: number; - name: string; - parameters: Json; - }; - Insert: { - created_at?: string; - id?: number; - name: string; - parameters?: Json; - }; - Update: { - created_at?: string; - id?: number; - name?: string; - parameters?: Json; - }; - Relationships: []; - }; - pd_flows: { - Row: { - created_at: string; - google_folder_id: string | null; - id: number; - parameters: Json; - pd_flow_template_id: number; - title: string | null; - }; - Insert: { - created_at?: string; - google_folder_id?: string | null; - id?: number; - parameters?: Json; - pd_flow_template_id: number; - title?: string | null; - }; - Update: { - created_at?: string; - google_folder_id?: string | null; - id?: number; - parameters?: Json; - pd_flow_template_id?: number; - title?: string | null; - }; - Relationships: [ { - foreignKeyName: 'pd_flows_pd_flow_template_id_fkey'; - columns: ['pd_flow_template_id']; + foreignKeyName: 'orders_rls_organization_id_fkey'; + columns: ['rls_organization_id']; isOneToOne: false; - referencedRelation: 'pd_flow_templates'; + referencedRelation: 'organizations'; referencedColumns: ['id']; }, ]; }; - pd_section_templates: { - Row: { - created_at: string; - id: number; - title: string | null; - }; - Insert: { - created_at?: string; - id?: number; - title?: string | null; - }; - Update: { - created_at?: string; - id?: number; - title?: string | null; - }; - Relationships: []; - }; - pd_sections: { + organizations: { Row: { created_at: string; + deleted_at: string | null; + developer_group_telegram_chat_id: string | null; + email: string | null; + epicollect_contract_last_sync_at: string | null; + epicollect_contract_survey_client_id: string | null; + epicollect_contract_survey_secret: string | null; + epicollect_contract_survey_slug: string | null; + formal_name: string | null; id: number; - title: string | null; + name: string; + organization_type: Database['public']['Enums']['organization_type_enum']; }; Insert: { created_at?: string; + deleted_at?: string | null; + developer_group_telegram_chat_id?: string | null; + email?: string | null; + epicollect_contract_last_sync_at?: string | null; + epicollect_contract_survey_client_id?: string | null; + epicollect_contract_survey_secret?: string | null; + epicollect_contract_survey_slug?: string | null; + formal_name?: string | null; id?: number; - title?: string | null; + name: string; + organization_type?: Database['public']['Enums']['organization_type_enum']; }; Update: { created_at?: string; + deleted_at?: string | null; + developer_group_telegram_chat_id?: string | null; + email?: string | null; + epicollect_contract_last_sync_at?: string | null; + epicollect_contract_survey_client_id?: string | null; + epicollect_contract_survey_secret?: string | null; + epicollect_contract_survey_slug?: string | null; + formal_name?: string | null; id?: number; - title?: string | null; + name?: string; + organization_type?: Database['public']['Enums']['organization_type_enum']; }; Relationships: []; }; @@ -3505,7 +2075,6 @@ export type Database = { operations_grid_id: number | null; organization_id: number | null; outline_geom: unknown; - pd_flow_id: number | null; }; Insert: { created_at?: string; @@ -3516,7 +2085,6 @@ export type Database = { operations_grid_id?: number | null; organization_id?: number | null; outline_geom?: unknown; - pd_flow_id?: number | null; }; Update: { created_at?: string; @@ -3527,7 +2095,6 @@ export type Database = { operations_grid_id?: number | null; organization_id?: number | null; outline_geom?: unknown; - pd_flow_id?: number | null; }; Relationships: [ { @@ -3544,13 +2111,6 @@ export type Database = { referencedRelation: 'organizations'; referencedColumns: ['id']; }, - { - foreignKeyName: 'pd_sites_pd_flow_id_fkey'; - columns: ['pd_flow_id']; - isOneToOne: false; - referencedRelation: 'pd_flows'; - referencedColumns: ['id']; - }, ]; }; poles: { @@ -3866,7 +2426,6 @@ export type Database = { connection_id: number | null; created_at: string; customer_id: number | null; - goldring_migration_id: number | null; id: number; identifier: string | null; lock_session: string | null; @@ -3882,7 +2441,6 @@ export type Database = { connection_id?: number | null; created_at?: string; customer_id?: number | null; - goldring_migration_id?: number | null; id?: number; identifier?: string | null; lock_session?: string | null; @@ -3898,7 +2456,6 @@ export type Database = { connection_id?: number | null; created_at?: string; customer_id?: number | null; - goldring_migration_id?: number | null; id?: number; identifier?: string | null; lock_session?: string | null; @@ -4002,31 +2559,6 @@ export type Database = { }, ]; }; - batch_commands: { - Row: { - created_at: string | null; - dcu_external_reference: string | null; - dcu_id: number | null; - directive_batch_execution_id: number | null; - directive_error: - | Database['public']['Enums']['directive_error_enum'] - | null; - directive_status: - | Database['public']['Enums']['directive_status_enum'] - | null; - directive_type: - | Database['public']['Enums']['directive_type_enum'] - | null; - id: number | null; - latitude: number | null; - location_geom: unknown; - longitude: number | null; - meter_external_reference: string | null; - meter_id: number | null; - meter_type: Database['public']['Enums']['meter_type_enum'] | null; - }; - Relationships: []; - }; customers_with_account: { Row: { account_id: number | null; @@ -4035,8 +2567,7 @@ export type Database = { full_name: string | null; gender: Database['public']['Enums']['gender_enum'] | null; generator_owned: - | Database['public']['Enums']['generator_type_enum'] - | null; + Database['public']['Enums']['generator_type_enum'] | null; grid_id: number | null; has_fully_paid_connection_fees: boolean | null; id: number | null; @@ -4071,22 +2602,16 @@ export type Database = { balance: number | null; balance_updated_at: string | null; communication_protocol: - | Database['public']['Enums']['communication_protocol_enum'] - | null; + Database['public']['Enums']['communication_protocol_enum'] | null; connection_id: number | null; coord_accuracy: number | null; created_at: string | null; - current_special_status: - | Database['public']['Enums']['directive_special_status_enum'] - | null; dcu_id: number | null; decoder_key: string | null; deleted_at: string | null; - device_id: number | null; external_reference: string | null; external_system: - | Database['public']['Enums']['external_system_enum'] - | null; + Database['public']['Enums']['external_system_enum'] | null; full_name: string | null; grid_id: number | null; id: number | null; @@ -4097,7 +2622,6 @@ export type Database = { is_manual_mode_on_updated_at: string | null; is_on: boolean | null; is_on_updated_at: string | null; - is_simulated: boolean | null; is_starred: boolean | null; is_test_mode_on: boolean | null; issue_check_execution_session: string | null; @@ -4119,15 +2643,12 @@ export type Database = { phone: string | null; pole_id: number | null; power: number | null; - power_down_count: number | null; - power_down_count_updated_at: string | null; power_limit: number | null; power_limit_hps_mode: number | null; power_limit_should_be: number | null; power_limit_should_be_updated_at: string | null; power_limit_updated_at: string | null; power_updated_at: string | null; - pulse_counter_kwh: number | null; rls_grid_id: number | null; rls_organization_id: number | null; should_be_on: boolean | null; @@ -4135,8 +2656,6 @@ export type Database = { version: string | null; voltage: number | null; voltage_updated_at: string | null; - watchdog_last_run_at: string | null; - watchdog_session: string | null; }; Relationships: [ { @@ -4181,13 +2700,6 @@ export type Database = { referencedRelation: 'connections'; referencedColumns: ['id']; }, - { - foreignKeyName: 'meters_device_id_fkey'; - columns: ['device_id']; - isOneToOne: true; - referencedRelation: 'devices'; - referencedColumns: ['id']; - }, { foreignKeyName: 'meters_rls_grid_id_fkey'; columns: ['rls_grid_id']; @@ -4226,154 +2738,56 @@ export type Database = { get_grid_status: { Args: { grid_id: number }; Returns: { - are_all_dcus_online: boolean; - are_all_dcus_under_high_load_threshold: boolean; customer_count: number; is_cabin_meter_credit_depleting: boolean; is_fs_on: boolean; is_hps_on: boolean; }[]; }; - lock_next_order: { Args: { uuid: string }; Returns: undefined }; lock_next_order_and_wallets: { Args: { uuid: string }; Returns: { id: number; }[]; }; - lock_next_pd_action: { Args: never; Returns: number }; + rls_check_if_admin_org_member: { Args: never; Returns: boolean }; rls_check_if_lender: { Args: never; Returns: boolean }; - rls_check_if_nxt_member: { Args: never; Returns: boolean }; rls_get_member_org_id: { Args: never; Returns: number }; + rls_org_id_from_agent: { Args: { agent_id: number }; Returns: number }; + rls_org_id_from_connection: { + Args: { connection_id: number }; + Returns: number; + }; + rls_org_id_from_customer: { + Args: { customer_id: number }; + Returns: number; + }; + rls_org_id_from_dcu: { Args: { dcu_id: number }; Returns: number }; + rls_org_id_from_grid: { Args: { grid_id: number }; Returns: number }; + rls_org_id_from_meter: { Args: { meter_id: number }; Returns: number }; }; Enums: { account_type_enum: 'AGENT' | 'MEMBER' | 'CUSTOMER'; communication_protocol_enum: 'CALIN_V1' | 'CALIN_V2' | 'CALIN_LORAWAN'; currency_enum: 'USD' | 'NGN' | 'EUR'; - directive_direction_enum: 'UP' | 'DOWN'; - directive_error_enum: - | 'GRID_DOWN' - | 'DCU_OFFLINE' - | 'NO_METER' - | 'NO_DCU' - | 'NO_GRID' - | 'NO_CONNECTION' - | 'NO_CUSTOMER'; - directive_phase_enum: 'A' | 'B' | 'C'; - directive_special_status_enum: - | 'POWER_LIMIT_BREACHED' - | 'CREDIT_EXHAUSTED' - | 'REMOTE_SWITCHED_OFF' - | 'OVER_VOLTAGE' - | 'METER_NOT_ACTIVATED' - | 'TAMPER' - | 'LOW_VOLTAGE'; - directive_status_enum: - | 'INITIALISED' - | 'PENDING' - | 'SENT_TO_API' - | 'RECEIVED_BY_API' - | 'SENT_TO_DCU' - | 'RECEIVED_BY_DCU' - | 'SENT_TO_METER' - | 'RECEIVED_BY_METER' - | 'SUCCESSFUL' - | 'FAILED' - | 'IGNORED' - | 'CANCELLED' - | 'TIMED_OUT' - | 'UNKNOWN'; - directive_type_enum: - | 'ON' - | 'OFF' - | 'READ_VOLTAGE' - | 'PLS' - | 'PLR' - | 'CLEAR_TAMPER' - | 'READ_METER_VERSION' - | 'TOP_UP' - | 'READ_CURRENT_CREDIT' - | 'READ_CURRENT' - | 'READ_POWER' - | 'READ_SPECIAL_STATUS' - | 'CLEAR_CREDIT' - | 'READ_RELAY_STATUS' - | 'READ_POWER_DOWN_COUNT' - | 'READ_TIME' - | 'READ_DATE' - | 'READ_VOLTAGE_A' - | 'READ_VOLTAGE_B' - | 'READ_VOLTAGE_C' - | 'READ_POWER_A' - | 'READ_POWER_B' - | 'READ_POWER_C' - | 'READ_CURRENT_A' - | 'READ_CURRENT_B' - | 'READ_CURRENT_C' - | 'READ_TOTAL_ACTIVE_KWH' - | 'READ_STATUS' - | 'SEND_TOKEN' - | 'READ_FRAUD_STATUS' - | 'READ_REPORT' - | 'WRITE_DATE' - | 'WRITE_TIME' - | 'CLEAR_TAMPER_TOKEN' - | 'READ_REPORT_UP' - | 'READ_REPORT_DOWN' - | 'READ_CREDIT_DOWN' - | 'READ_CREDIT_UP' - | 'READ_VOLTAGE_UP' - | 'READ_VOLTAGE_DOWN' - | 'READ_POWER_UP' - | 'READ_POWER_DOWN' - | 'READ_CURRENT_UP' - | 'READ_CURRENT_DOWN' - | 'CLEAR_TAMPER_UP' - | 'CLEAR_TAMPER_DOWN' - | 'TOP_UP_DOWN' - | 'POWER_LIMIT_SET_UP' - | 'POWER_LIMIT_SET_DOWN' - | 'OPEN_RELAY_UP' - | 'OPEN_RELAY_DOWN' - | 'CLOSE_RELAY_UP' - | 'CLOSE_RELAY_DOWN' - | 'READ_VOLTAGE_A_UP' - | 'READ_VOLTAGE_A_DOWN' - | 'READ_POWER_A_UP' - | 'READ_POWER_A_DOWN' - | 'READ_CURRENT_A_UP' - | 'READ_CURRENT_A_DOWN' - | 'UNKNOWN' - | 'CLEAR_CREDIT_DOWN' - | 'TOP_UP_KWH' - | 'TOKEN_ACCEPTED' - | 'TOKEN_REJECTED' - | 'ON_OFF_ACCEPTED' - | 'ON_OFF_REJECTED'; external_system_enum: - | 'STEAMACO' | 'CALIN' | 'SOLCAST' | 'VICTRON' | 'FLUTTERWAVE' | 'AFRICASTALKING' - | 'JOTFORM' | 'EPICOLLECT' | 'JIRA' | 'TELEGRAM' | 'ZEROTIER' | 'MAKE' | 'FLOW_XO' - | 'SENDGRID' - | 'ACREL'; + | 'SENDGRID'; fs_command_type_enum: 'ON' | 'OFF'; gender_enum: 'MALE' | 'FEMALE'; generator_type_enum: 'SMALL' | 'LARGE'; id_document_type_enum: - | 'PASSPORT' - | 'NATIONAL_ID' - | 'DRIVING_LICENSE' - | 'VOTERS_CARD'; + 'PASSPORT' | 'NATIONAL_ID' | 'DRIVING_LICENSE' | 'VOTERS_CARD'; issue_status_enum: 'OPEN' | 'CLOSED' | 'OVERRIDDEN'; issue_type_enum: | 'NO_COMMUNICATION' @@ -4412,15 +2826,7 @@ export type Database = { | 'SALES' | 'TECH'; meter_commissioning_status_enum: - | 'PENDING' - | 'PROCESSING' - | 'SUCCESSFUL' - | 'FAILED'; - meter_credit_transfer_status_enum: - | 'PENDING' - | 'PROCESSING' - | 'SUCCESSFUL' - | 'FAILED'; + 'PENDING' | 'PROCESSING' | 'SUCCESSFUL' | 'FAILED'; meter_interaction_status_enum: | 'QUEUED' | 'ABORTED' @@ -4473,7 +2879,6 @@ export type Database = { | 'GRID_REVENUE' | 'PASSWORD_RESET' | 'INVITE' - | 'AUTO_PAYOUT_GENRATION_REPORT' | 'CREDIT_SENT' | 'CREDIT_RECEIVED' | 'METER_TOPPED_UP' @@ -4503,23 +2908,10 @@ export type Database = { | 'ORGANIZATION_TOPUP' | 'ORGANIZATION_WITHDRAWAL' | 'CUSTOMER_TOPUP'; - organization_type_enum: 'SOLAR_DEVELOPER' | 'LENDER' | 'DATA_AGGREGATOR'; + organization_type_enum: + 'SOLAR_DEVELOPER' | 'LENDER' | 'DATA_AGGREGATOR' | 'PLATFORM_OPERATOR'; payment_channel_enum: 'USSD' | 'AYRTON' | 'NIFFLER' | 'TELEGRAM'; payment_method_enum: 'CREDIT_CARD' | 'USSD' | 'BANK_TRANSFER'; - payout_status_enum: - | 'INITIALISED' - | 'WAITING_FOR_APPROVAL' - | 'PROCESSING' - | 'SUCCESSFUL' - | 'FAILED'; - pd_action_status_enum: - | 'GENERATING' - | 'GENERATION_FAILED' - | 'GENERATION_COMPLETED' - | 'ACTIONABLE' - | 'ACTION_COMPLETED'; - pd_action_type_enum: 'UPLOAD' | 'TEMPLATE' | 'EXTERNAL' | 'START' | 'END'; - pd_document_type_enum: 'GOOGLE_SHEETS' | 'GOOGLE_DOCS'; solcast_cache_request_type_enum: 'ESTIMATED_ACTUALS' | 'FORECAST'; transaction_status_enum: 'SUCCESSFUL' | 'FAILED'; wallet_type_enum: 'VIRTUAL' | 'REAL'; @@ -4548,12 +2940,12 @@ export type Tables< DefaultSchemaTableNameOrOptions extends | keyof (DefaultSchema['Tables'] & DefaultSchema['Views']) | { schema: keyof DatabaseWithoutInternals }, - TableName extends DefaultSchemaTableNameOrOptions extends { + TableName extends (DefaultSchemaTableNameOrOptions extends { schema: keyof DatabaseWithoutInternals; } ? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'] & DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Views']) - : never = never, + : never) = never, > = DefaultSchemaTableNameOrOptions extends { schema: keyof DatabaseWithoutInternals; } @@ -4575,13 +2967,12 @@ export type Tables< export type TablesInsert< DefaultSchemaTableNameOrOptions extends - | keyof DefaultSchema['Tables'] - | { schema: keyof DatabaseWithoutInternals }, - TableName extends DefaultSchemaTableNameOrOptions extends { + keyof DefaultSchema['Tables'] | { schema: keyof DatabaseWithoutInternals }, + TableName extends (DefaultSchemaTableNameOrOptions extends { schema: keyof DatabaseWithoutInternals; } ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'] - : never = never, + : never) = never, > = DefaultSchemaTableNameOrOptions extends { schema: keyof DatabaseWithoutInternals; } @@ -4600,13 +2991,12 @@ export type TablesInsert< export type TablesUpdate< DefaultSchemaTableNameOrOptions extends - | keyof DefaultSchema['Tables'] - | { schema: keyof DatabaseWithoutInternals }, - TableName extends DefaultSchemaTableNameOrOptions extends { + keyof DefaultSchema['Tables'] | { schema: keyof DatabaseWithoutInternals }, + TableName extends (DefaultSchemaTableNameOrOptions extends { schema: keyof DatabaseWithoutInternals; } ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'] - : never = never, + : never) = never, > = DefaultSchemaTableNameOrOptions extends { schema: keyof DatabaseWithoutInternals; } @@ -4625,13 +3015,12 @@ export type TablesUpdate< export type Enums< DefaultSchemaEnumNameOrOptions extends - | keyof DefaultSchema['Enums'] - | { schema: keyof DatabaseWithoutInternals }, - EnumName extends DefaultSchemaEnumNameOrOptions extends { + keyof DefaultSchema['Enums'] | { schema: keyof DatabaseWithoutInternals }, + EnumName extends (DefaultSchemaEnumNameOrOptions extends { schema: keyof DatabaseWithoutInternals; } ? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions['schema']]['Enums'] - : never = never, + : never) = never, > = DefaultSchemaEnumNameOrOptions extends { schema: keyof DatabaseWithoutInternals; } @@ -4644,11 +3033,11 @@ export type CompositeTypes< PublicCompositeTypeNameOrOptions extends | keyof DefaultSchema['CompositeTypes'] | { schema: keyof DatabaseWithoutInternals }, - CompositeTypeName extends PublicCompositeTypeNameOrOptions extends { + CompositeTypeName extends (PublicCompositeTypeNameOrOptions extends { schema: keyof DatabaseWithoutInternals; } ? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions['schema']]['CompositeTypes'] - : never = never, + : never) = never, > = PublicCompositeTypeNameOrOptions extends { schema: keyof DatabaseWithoutInternals; } @@ -4663,118 +3052,12 @@ export const Constants = { account_type_enum: [ 'AGENT', 'MEMBER', 'CUSTOMER' ], communication_protocol_enum: [ 'CALIN_V1', 'CALIN_V2', 'CALIN_LORAWAN' ], currency_enum: [ 'USD', 'NGN', 'EUR' ], - directive_direction_enum: [ 'UP', 'DOWN' ], - directive_error_enum: [ - 'GRID_DOWN', - 'DCU_OFFLINE', - 'NO_METER', - 'NO_DCU', - 'NO_GRID', - 'NO_CONNECTION', - 'NO_CUSTOMER', - ], - directive_phase_enum: [ 'A', 'B', 'C' ], - directive_special_status_enum: [ - 'POWER_LIMIT_BREACHED', - 'CREDIT_EXHAUSTED', - 'REMOTE_SWITCHED_OFF', - 'OVER_VOLTAGE', - 'METER_NOT_ACTIVATED', - 'TAMPER', - 'LOW_VOLTAGE', - ], - directive_status_enum: [ - 'INITIALISED', - 'PENDING', - 'SENT_TO_API', - 'RECEIVED_BY_API', - 'SENT_TO_DCU', - 'RECEIVED_BY_DCU', - 'SENT_TO_METER', - 'RECEIVED_BY_METER', - 'SUCCESSFUL', - 'FAILED', - 'IGNORED', - 'CANCELLED', - 'TIMED_OUT', - 'UNKNOWN', - ], - directive_type_enum: [ - 'ON', - 'OFF', - 'READ_VOLTAGE', - 'PLS', - 'PLR', - 'CLEAR_TAMPER', - 'READ_METER_VERSION', - 'TOP_UP', - 'READ_CURRENT_CREDIT', - 'READ_CURRENT', - 'READ_POWER', - 'READ_SPECIAL_STATUS', - 'CLEAR_CREDIT', - 'READ_RELAY_STATUS', - 'READ_POWER_DOWN_COUNT', - 'READ_TIME', - 'READ_DATE', - 'READ_VOLTAGE_A', - 'READ_VOLTAGE_B', - 'READ_VOLTAGE_C', - 'READ_POWER_A', - 'READ_POWER_B', - 'READ_POWER_C', - 'READ_CURRENT_A', - 'READ_CURRENT_B', - 'READ_CURRENT_C', - 'READ_TOTAL_ACTIVE_KWH', - 'READ_STATUS', - 'SEND_TOKEN', - 'READ_FRAUD_STATUS', - 'READ_REPORT', - 'WRITE_DATE', - 'WRITE_TIME', - 'CLEAR_TAMPER_TOKEN', - 'READ_REPORT_UP', - 'READ_REPORT_DOWN', - 'READ_CREDIT_DOWN', - 'READ_CREDIT_UP', - 'READ_VOLTAGE_UP', - 'READ_VOLTAGE_DOWN', - 'READ_POWER_UP', - 'READ_POWER_DOWN', - 'READ_CURRENT_UP', - 'READ_CURRENT_DOWN', - 'CLEAR_TAMPER_UP', - 'CLEAR_TAMPER_DOWN', - 'TOP_UP_DOWN', - 'POWER_LIMIT_SET_UP', - 'POWER_LIMIT_SET_DOWN', - 'OPEN_RELAY_UP', - 'OPEN_RELAY_DOWN', - 'CLOSE_RELAY_UP', - 'CLOSE_RELAY_DOWN', - 'READ_VOLTAGE_A_UP', - 'READ_VOLTAGE_A_DOWN', - 'READ_POWER_A_UP', - 'READ_POWER_A_DOWN', - 'READ_CURRENT_A_UP', - 'READ_CURRENT_A_DOWN', - 'UNKNOWN', - 'CLEAR_CREDIT_DOWN', - 'TOP_UP_KWH', - 'TOKEN_ACCEPTED', - 'TOKEN_REJECTED', - 'ON_OFF_ACCEPTED', - 'ON_OFF_REJECTED', - ], external_system_enum: [ - 'STEAMACO', 'CALIN', 'SOLCAST', 'VICTRON', 'FLUTTERWAVE', 'AFRICASTALKING', - 'JOTFORM', 'EPICOLLECT', 'JIRA', 'TELEGRAM', @@ -4782,7 +3065,6 @@ export const Constants = { 'MAKE', 'FLOW_XO', 'SENDGRID', - 'ACREL', ], fs_command_type_enum: [ 'ON', 'OFF' ], gender_enum: [ 'MALE', 'FEMALE' ], @@ -4838,12 +3120,6 @@ export const Constants = { 'SUCCESSFUL', 'FAILED', ], - meter_credit_transfer_status_enum: [ - 'PENDING', - 'PROCESSING', - 'SUCCESSFUL', - 'FAILED', - ], meter_interaction_status_enum: [ 'QUEUED', 'ABORTED', @@ -4899,7 +3175,6 @@ export const Constants = { 'GRID_REVENUE', 'PASSWORD_RESET', 'INVITE', - 'AUTO_PAYOUT_GENRATION_REPORT', 'CREDIT_SENT', 'CREDIT_RECEIVED', 'METER_TOPPED_UP', @@ -4933,25 +3208,14 @@ export const Constants = { 'ORGANIZATION_WITHDRAWAL', 'CUSTOMER_TOPUP', ], - organization_type_enum: [ 'SOLAR_DEVELOPER', 'LENDER', 'DATA_AGGREGATOR' ], + organization_type_enum: [ + 'SOLAR_DEVELOPER', + 'LENDER', + 'DATA_AGGREGATOR', + 'PLATFORM_OPERATOR', + ], payment_channel_enum: [ 'USSD', 'AYRTON', 'NIFFLER', 'TELEGRAM' ], payment_method_enum: [ 'CREDIT_CARD', 'USSD', 'BANK_TRANSFER' ], - payout_status_enum: [ - 'INITIALISED', - 'WAITING_FOR_APPROVAL', - 'PROCESSING', - 'SUCCESSFUL', - 'FAILED', - ], - pd_action_status_enum: [ - 'GENERATING', - 'GENERATION_FAILED', - 'GENERATION_COMPLETED', - 'ACTIONABLE', - 'ACTION_COMPLETED', - ], - pd_action_type_enum: [ 'UPLOAD', 'TEMPLATE', 'EXTERNAL', 'START', 'END' ], - pd_document_type_enum: [ 'GOOGLE_SHEETS', 'GOOGLE_DOCS' ], solcast_cache_request_type_enum: [ 'ESTIMATED_ACTUALS', 'FORECAST' ], transaction_status_enum: [ 'SUCCESSFUL', 'FAILED' ], wallet_type_enum: [ 'VIRTUAL', 'REAL' ], @@ -4967,7 +3231,6 @@ export const Constants = { }, } as const; -// Schema: __InternalSupabase // Schema: public // Enums export type AccountTypeEnum = Database['public']['Enums']['account_type_enum']; @@ -4977,24 +3240,6 @@ export type CommunicationProtocolEnum = export type CurrencyEnum = Database['public']['Enums']['currency_enum']; -export type DirectiveDirectionEnum = - Database['public']['Enums']['directive_direction_enum']; - -export type DirectiveErrorEnum = - Database['public']['Enums']['directive_error_enum']; - -export type DirectivePhaseEnum = - Database['public']['Enums']['directive_phase_enum']; - -export type DirectiveSpecialStatusEnum = - Database['public']['Enums']['directive_special_status_enum']; - -export type DirectiveStatusEnum = - Database['public']['Enums']['directive_status_enum']; - -export type DirectiveTypeEnum = - Database['public']['Enums']['directive_type_enum']; - export type ExternalSystemEnum = Database['public']['Enums']['external_system_enum']; @@ -5018,9 +3263,6 @@ export type MemberTypeEnum = Database['public']['Enums']['member_type_enum']; export type MeterCommissioningStatusEnum = Database['public']['Enums']['meter_commissioning_status_enum']; -export type MeterCreditTransferStatusEnum = - Database['public']['Enums']['meter_credit_transfer_status_enum']; - export type MeterInteractionStatusEnum = Database['public']['Enums']['meter_interaction_status_enum']; @@ -5060,18 +3302,6 @@ export type PaymentChannelEnum = export type PaymentMethodEnum = Database['public']['Enums']['payment_method_enum']; -export type PayoutStatusEnum = - Database['public']['Enums']['payout_status_enum']; - -export type PdActionStatusEnum = - Database['public']['Enums']['pd_action_status_enum']; - -export type PdActionTypeEnum = - Database['public']['Enums']['pd_action_type_enum']; - -export type PdDocumentTypeEnum = - Database['public']['Enums']['pd_document_type_enum']; - export type SolcastCacheRequestTypeEnum = Database['public']['Enums']['solcast_cache_request_type_enum']; @@ -5099,19 +3329,6 @@ export type Audit = Database['public']['Tables']['audits']['Row']; export type InsertAudit = Database['public']['Tables']['audits']['Insert']; export type UpdateAudit = Database['public']['Tables']['audits']['Update']; -export type AutopilotExecution = - Database['public']['Tables']['autopilot_executions']['Row']; -export type InsertAutopilotExecution = - Database['public']['Tables']['autopilot_executions']['Insert']; -export type UpdateAutopilotExecution = - Database['public']['Tables']['autopilot_executions']['Update']; - -export type BankAccount = Database['public']['Tables']['bank_accounts']['Row']; -export type InsertBankAccount = - Database['public']['Tables']['bank_accounts']['Insert']; -export type UpdateBankAccount = - Database['public']['Tables']['bank_accounts']['Update']; - export type Bank = Database['public']['Tables']['banks']['Row']; export type InsertBank = Database['public']['Tables']['banks']['Insert']; export type UpdateBank = Database['public']['Tables']['banks']['Update']; @@ -5139,59 +3356,12 @@ export type Dcus = Database['public']['Tables']['dcus']['Row']; export type InsertDcus = Database['public']['Tables']['dcus']['Insert']; export type UpdateDcus = Database['public']['Tables']['dcus']['Update']; -export type DeviceLog = Database['public']['Tables']['device_logs']['Row']; -export type InsertDeviceLog = - Database['public']['Tables']['device_logs']['Insert']; -export type UpdateDeviceLog = - Database['public']['Tables']['device_logs']['Update']; - -export type DeviceType = Database['public']['Tables']['device_types']['Row']; -export type InsertDeviceType = - Database['public']['Tables']['device_types']['Insert']; -export type UpdateDeviceType = - Database['public']['Tables']['device_types']['Update']; - -export type Device = Database['public']['Tables']['devices']['Row']; -export type InsertDevice = Database['public']['Tables']['devices']['Insert']; -export type UpdateDevice = Database['public']['Tables']['devices']['Update']; - -export type DirectiveBatchExecution = - Database['public']['Tables']['directive_batch_executions']['Row']; -export type InsertDirectiveBatchExecution = - Database['public']['Tables']['directive_batch_executions']['Insert']; -export type UpdateDirectiveBatchExecution = - Database['public']['Tables']['directive_batch_executions']['Update']; - -export type DirectiveBatch = - Database['public']['Tables']['directive_batches']['Row']; -export type InsertDirectiveBatch = - Database['public']['Tables']['directive_batches']['Insert']; -export type UpdateDirectiveBatch = - Database['public']['Tables']['directive_batches']['Update']; - -export type DirectiveWatchdogSession = - Database['public']['Tables']['directive_watchdog_sessions']['Row']; -export type InsertDirectiveWatchdogSession = - Database['public']['Tables']['directive_watchdog_sessions']['Insert']; -export type UpdateDirectiveWatchdogSession = - Database['public']['Tables']['directive_watchdog_sessions']['Update']; - -export type Directive = Database['public']['Tables']['directives']['Row']; -export type InsertDirective = - Database['public']['Tables']['directives']['Insert']; -export type UpdateDirective = - Database['public']['Tables']['directives']['Update']; - export type EnergyCabin = Database['public']['Tables']['energy_cabins']['Row']; export type InsertEnergyCabin = Database['public']['Tables']['energy_cabins']['Insert']; export type UpdateEnergyCabin = Database['public']['Tables']['energy_cabins']['Update']; -export type Feature = Database['public']['Tables']['features']['Row']; -export type InsertFeature = Database['public']['Tables']['features']['Insert']; -export type UpdateFeature = Database['public']['Tables']['features']['Update']; - export type Grid = Database['public']['Tables']['grids']['Row']; export type InsertGrid = Database['public']['Tables']['grids']['Insert']; export type UpdateGrid = Database['public']['Tables']['grids']['Update']; @@ -5200,24 +3370,24 @@ export type Issue = Database['public']['Tables']['issues']['Row']; export type InsertIssue = Database['public']['Tables']['issues']['Insert']; export type UpdateIssue = Database['public']['Tables']['issues']['Update']; -export type LorawanDirective = - Database['public']['Tables']['lorawan_directives']['Row']; -export type InsertLorawanDirective = - Database['public']['Tables']['lorawan_directives']['Insert']; -export type UpdateLorawanDirective = - Database['public']['Tables']['lorawan_directives']['Update']; - -export type MemberFeature = - Database['public']['Tables']['member_feature']['Row']; -export type InsertMemberFeature = - Database['public']['Tables']['member_feature']['Insert']; -export type UpdateMemberFeature = - Database['public']['Tables']['member_feature']['Update']; - export type Member = Database['public']['Tables']['members']['Row']; export type InsertMember = Database['public']['Tables']['members']['Insert']; export type UpdateMember = Database['public']['Tables']['members']['Update']; +export type MeterCommandBatchExecution = + Database['public']['Tables']['meter_command_batch_executions']['Row']; +export type InsertMeterCommandBatchExecution = + Database['public']['Tables']['meter_command_batch_executions']['Insert']; +export type UpdateMeterCommandBatchExecution = + Database['public']['Tables']['meter_command_batch_executions']['Update']; + +export type MeterCommandBatch = + Database['public']['Tables']['meter_command_batches']['Row']; +export type InsertMeterCommandBatch = + Database['public']['Tables']['meter_command_batches']['Insert']; +export type UpdateMeterCommandBatch = + Database['public']['Tables']['meter_command_batches']['Update']; + export type MeterCommissioning = Database['public']['Tables']['meter_commissionings']['Row']; export type InsertMeterCommissioning = @@ -5225,13 +3395,6 @@ export type InsertMeterCommissioning = export type UpdateMeterCommissioning = Database['public']['Tables']['meter_commissionings']['Update']; -export type MeterCreditTransfer = - Database['public']['Tables']['meter_credit_transfers']['Row']; -export type InsertMeterCreditTransfer = - Database['public']['Tables']['meter_credit_transfers']['Insert']; -export type UpdateMeterCreditTransfer = - Database['public']['Tables']['meter_credit_transfers']['Update']; - export type MeterInteraction = Database['public']['Tables']['meter_interactions']['Row']; export type InsertMeterInteraction = @@ -5257,12 +3420,6 @@ export type Meter = Database['public']['Tables']['meters']['Row']; export type InsertMeter = Database['public']['Tables']['meters']['Insert']; export type UpdateMeter = Database['public']['Tables']['meters']['Update']; -export type Migration = Database['public']['Tables']['migrations']['Row']; -export type InsertMigration = - Database['public']['Tables']['migrations']['Insert']; -export type UpdateMigration = - Database['public']['Tables']['migrations']['Update']; - export type Mppt = Database['public']['Tables']['mppts']['Row']; export type InsertMppt = Database['public']['Tables']['mppts']['Insert']; export type UpdateMppt = Database['public']['Tables']['mppts']['Update']; @@ -5294,64 +3451,6 @@ export type InsertOrganization = export type UpdateOrganization = Database['public']['Tables']['organizations']['Update']; -export type Payout = Database['public']['Tables']['payouts']['Row']; -export type InsertPayout = Database['public']['Tables']['payouts']['Insert']; -export type UpdatePayout = Database['public']['Tables']['payouts']['Update']; - -export type PdActionTemplate = - Database['public']['Tables']['pd_action_templates']['Row']; -export type InsertPdActionTemplate = - Database['public']['Tables']['pd_action_templates']['Insert']; -export type UpdatePdActionTemplate = - Database['public']['Tables']['pd_action_templates']['Update']; - -export type PdAction = Database['public']['Tables']['pd_actions']['Row']; -export type InsertPdAction = - Database['public']['Tables']['pd_actions']['Insert']; -export type UpdatePdAction = - Database['public']['Tables']['pd_actions']['Update']; - -export type PdAudit = Database['public']['Tables']['pd_audits']['Row']; -export type InsertPdAudit = Database['public']['Tables']['pd_audits']['Insert']; -export type UpdatePdAudit = Database['public']['Tables']['pd_audits']['Update']; - -export type PdDocumentTemplate = - Database['public']['Tables']['pd_document_templates']['Row']; -export type InsertPdDocumentTemplate = - Database['public']['Tables']['pd_document_templates']['Insert']; -export type UpdatePdDocumentTemplate = - Database['public']['Tables']['pd_document_templates']['Update']; - -export type PdDocument = Database['public']['Tables']['pd_documents']['Row']; -export type InsertPdDocument = - Database['public']['Tables']['pd_documents']['Insert']; -export type UpdatePdDocument = - Database['public']['Tables']['pd_documents']['Update']; - -export type PdFlowTemplate = - Database['public']['Tables']['pd_flow_templates']['Row']; -export type InsertPdFlowTemplate = - Database['public']['Tables']['pd_flow_templates']['Insert']; -export type UpdatePdFlowTemplate = - Database['public']['Tables']['pd_flow_templates']['Update']; - -export type PdFlow = Database['public']['Tables']['pd_flows']['Row']; -export type InsertPdFlow = Database['public']['Tables']['pd_flows']['Insert']; -export type UpdatePdFlow = Database['public']['Tables']['pd_flows']['Update']; - -export type PdSectionTemplate = - Database['public']['Tables']['pd_section_templates']['Row']; -export type InsertPdSectionTemplate = - Database['public']['Tables']['pd_section_templates']['Insert']; -export type UpdatePdSectionTemplate = - Database['public']['Tables']['pd_section_templates']['Update']; - -export type PdSection = Database['public']['Tables']['pd_sections']['Row']; -export type InsertPdSection = - Database['public']['Tables']['pd_sections']['Insert']; -export type UpdatePdSection = - Database['public']['Tables']['pd_sections']['Update']; - export type PdSiteSubmission = Database['public']['Tables']['pd_site_submissions']['Row']; export type InsertPdSiteSubmission = @@ -5404,8 +3503,6 @@ export type UpdateWallet = Database['public']['Tables']['wallets']['Update']; export type AgentWithAccount = Database['public']['Views']['agents_with_account']['Row']; -export type BatchCommand = Database['public']['Views']['batch_commands']['Row']; - export type CustomerWithAccount = Database['public']['Views']['customers_with_account']['Row']; @@ -5428,32 +3525,52 @@ export type ArgsGetGridStatus = export type ReturnTypeGetGridStatus = Database['public']['Functions']['get_grid_status']['Returns']; -export type ArgsLockNextOrder = - Database['public']['Functions']['lock_next_order']['Args']; -export type ReturnTypeLockNextOrder = - Database['public']['Functions']['lock_next_order']['Returns']; - export type ArgsLockNextOrderAndWallet = Database['public']['Functions']['lock_next_order_and_wallets']['Args']; export type ReturnTypeLockNextOrderAndWallet = Database['public']['Functions']['lock_next_order_and_wallets']['Returns']; -export type ArgsLockNextPdAction = - Database['public']['Functions']['lock_next_pd_action']['Args']; -export type ReturnTypeLockNextPdAction = - Database['public']['Functions']['lock_next_pd_action']['Returns']; +export type ArgsRlCheckIfAdminOrgMember = + Database['public']['Functions']['rls_check_if_admin_org_member']['Args']; +export type ReturnTypeRlCheckIfAdminOrgMember = + Database['public']['Functions']['rls_check_if_admin_org_member']['Returns']; export type ArgsRlCheckIfLender = Database['public']['Functions']['rls_check_if_lender']['Args']; export type ReturnTypeRlCheckIfLender = Database['public']['Functions']['rls_check_if_lender']['Returns']; -export type ArgsRlCheckIfNxtMember = - Database['public']['Functions']['rls_check_if_nxt_member']['Args']; -export type ReturnTypeRlCheckIfNxtMember = - Database['public']['Functions']['rls_check_if_nxt_member']['Returns']; - export type ArgsRlGetMemberOrgId = Database['public']['Functions']['rls_get_member_org_id']['Args']; export type ReturnTypeRlGetMemberOrgId = Database['public']['Functions']['rls_get_member_org_id']['Returns']; + +export type ArgsRlOrgIdFromAgent = + Database['public']['Functions']['rls_org_id_from_agent']['Args']; +export type ReturnTypeRlOrgIdFromAgent = + Database['public']['Functions']['rls_org_id_from_agent']['Returns']; + +export type ArgsRlOrgIdFromConnection = + Database['public']['Functions']['rls_org_id_from_connection']['Args']; +export type ReturnTypeRlOrgIdFromConnection = + Database['public']['Functions']['rls_org_id_from_connection']['Returns']; + +export type ArgsRlOrgIdFromCustomer = + Database['public']['Functions']['rls_org_id_from_customer']['Args']; +export type ReturnTypeRlOrgIdFromCustomer = + Database['public']['Functions']['rls_org_id_from_customer']['Returns']; + +export type ArgsRlOrgIdFromDcu = + Database['public']['Functions']['rls_org_id_from_dcu']['Args']; +export type ReturnTypeRlOrgIdFromDcu = + Database['public']['Functions']['rls_org_id_from_dcu']['Returns']; + +export type ArgsRlOrgIdFromGrid = + Database['public']['Functions']['rls_org_id_from_grid']['Args']; +export type ReturnTypeRlOrgIdFromGrid = + Database['public']['Functions']['rls_org_id_from_grid']['Returns']; + +export type ArgsRlOrgIdFromMeter = + Database['public']['Functions']['rls_org_id_from_meter']['Args']; +export type ReturnTypeRlOrgIdFromMeter = + Database['public']['Functions']['rls_org_id_from_meter']['Returns']; diff --git a/libs/core/test/unit/config/__fixtures__/from-default.config.json b/libs/core/test/unit/config/__fixtures__/from-default.config.json new file mode 100644 index 0000000..43fa908 --- /dev/null +++ b/libs/core/test/unit/config/__fixtures__/from-default.config.json @@ -0,0 +1,8 @@ +{ + "$schemaVersion": "1", + "public": { + "platformName": "From bundled default" + }, + "capabilities": {}, + "integrations": {} +} diff --git a/libs/core/test/unit/config/__fixtures__/from-path.config.json b/libs/core/test/unit/config/__fixtures__/from-path.config.json new file mode 100644 index 0000000..6c6d204 --- /dev/null +++ b/libs/core/test/unit/config/__fixtures__/from-path.config.json @@ -0,0 +1,8 @@ +{ + "$schemaVersion": "1", + "public": { + "platformName": "From NXT_CONFIG_PATH" + }, + "capabilities": {}, + "integrations": {} +} diff --git a/libs/core/test/unit/config/__fixtures__/invalid-schema-version.config.json b/libs/core/test/unit/config/__fixtures__/invalid-schema-version.config.json new file mode 100644 index 0000000..a94e72c --- /dev/null +++ b/libs/core/test/unit/config/__fixtures__/invalid-schema-version.config.json @@ -0,0 +1,8 @@ +{ + "$schemaVersion": "2", + "public": { + "platformName": "Should be rejected" + }, + "capabilities": {}, + "integrations": {} +} diff --git a/libs/core/test/unit/config/index.spec.ts b/libs/core/test/unit/config/index.spec.ts new file mode 100644 index 0000000..3556df6 --- /dev/null +++ b/libs/core/test/unit/config/index.spec.ts @@ -0,0 +1,21 @@ +import { getConfig, setConfig } from '../../../src/config/index.js'; +import type { NxtConfig } from '../../../src/config/schema.js'; + +const testConfig: NxtConfig = Object.freeze({ + $schemaVersion: '1', + public: { platformName: 'Test' }, + capabilities: {}, + integrations: {}, +}); + +describe('getConfig / setConfig', () => { + it('throws a clear error when called before configuration is loaded', () => { + expect(() => getConfig()).toThrow(/before configuration was loaded/); + }); + + it('returns the value set via setConfig (the test override pattern)', () => { + setConfig(testConfig); + + expect(getConfig()).toBe(testConfig); + }); +}); diff --git a/libs/core/test/unit/config/loader.spec.ts b/libs/core/test/unit/config/loader.spec.ts new file mode 100644 index 0000000..68bccf1 --- /dev/null +++ b/libs/core/test/unit/config/loader.spec.ts @@ -0,0 +1,67 @@ +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { loadConfig } from '../../../src/config/loader.js'; + +const FIXTURES_DIR = join(dirname(fileURLToPath(import.meta.url)), '__fixtures__'); +const FROM_PATH_FIXTURE = join(FIXTURES_DIR, 'from-path.config.json'); +const FROM_DEFAULT_FIXTURE = join(FIXTURES_DIR, 'from-default.config.json'); +const INVALID_SCHEMA_VERSION_FIXTURE = join(FIXTURES_DIR, 'invalid-schema-version.config.json'); + +const INLINE_JSON = JSON.stringify({ + $schemaVersion: '1', + public: { platformName: 'From NXT_CONFIG_JSON' }, + capabilities: {}, + integrations: {}, +}); + +describe('loadConfig', () => { + const originalEnv = { ...process.env }; + + afterEach(() => { + process.env = { ...originalEnv }; + }); + + it('resolves from the bundled default when no env var is set', () => { + delete process.env.NXT_CONFIG_JSON; + delete process.env.NXT_CONFIG_PATH; + + const config = loadConfig({ defaultConfigPath: FROM_DEFAULT_FIXTURE }); + + expect(config.public.platformName).toBe('From bundled default'); + }); + + it('prefers NXT_CONFIG_PATH over the bundled default', () => { + delete process.env.NXT_CONFIG_JSON; + process.env.NXT_CONFIG_PATH = FROM_PATH_FIXTURE; + + const config = loadConfig({ defaultConfigPath: FROM_DEFAULT_FIXTURE }); + + expect(config.public.platformName).toBe('From NXT_CONFIG_PATH'); + }); + + it('prefers NXT_CONFIG_JSON over NXT_CONFIG_PATH and the bundled default', () => { + process.env.NXT_CONFIG_JSON = INLINE_JSON; + process.env.NXT_CONFIG_PATH = FROM_PATH_FIXTURE; + + const config = loadConfig({ defaultConfigPath: FROM_DEFAULT_FIXTURE }); + + expect(config.public.platformName).toBe('From NXT_CONFIG_JSON'); + }); + + it('rejects a $schemaVersion mismatch with a clear, path-based error', () => { + delete process.env.NXT_CONFIG_JSON; + process.env.NXT_CONFIG_PATH = INVALID_SCHEMA_VERSION_FIXTURE; + + expect(() => loadConfig({ defaultConfigPath: FROM_DEFAULT_FIXTURE })).toThrow(/\$schemaVersion/); + }); + + it('freezes the resolved configuration', () => { + delete process.env.NXT_CONFIG_JSON; + delete process.env.NXT_CONFIG_PATH; + + const config = loadConfig({ defaultConfigPath: FROM_DEFAULT_FIXTURE }); + + expect(Object.isFrozen(config)).toBe(true); + }); +}); diff --git a/libs/core/tsconfig.json b/libs/core/tsconfig.json index 3fbfb6e..62ebbd9 100644 --- a/libs/core/tsconfig.json +++ b/libs/core/tsconfig.json @@ -1,14 +1,5 @@ { "extends": "../../tsconfig.base.json", - "compilerOptions": { - "module": "commonjs", - "emitDecoratorMetadata": true, - "esModuleInterop": true, - "composite": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true - }, "files": [], "include": [], "references": [ @@ -19,4 +10,4 @@ "path": "./tsconfig.spec.json" } ] -} \ No newline at end of file +} diff --git a/libs/core/tsconfig.lib.json b/libs/core/tsconfig.lib.json index 119777d..030eb26 100644 --- a/libs/core/tsconfig.lib.json +++ b/libs/core/tsconfig.lib.json @@ -1,27 +1,23 @@ { - "extends": "./tsconfig.json", + "extends": "../../tsconfig.base.json", "compilerOptions": { - "outDir": "../../dist/out-tsc", - "declaration": true, + "rootDir": "src", + "outDir": "dist", + "tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo", + "emitDeclarationOnly": false, + "forceConsistentCasingInFileNames": true, "types": [ "node" - ], - "target": "es2021", - "composite": true, - "declarationMap": true + ] }, "include": [ - "src/**/*.ts", - "migration/1690451156799-init.ts" + "src/**/*.ts" ], + "references": [], "exclude": [ "jest.config.ts", + "jest.config.cts", "src/**/*.spec.ts", "src/**/*.test.ts" - ], - "references": [ - { - "path": "../helpers/tsconfig.lib.json" - } ] } diff --git a/libs/core/tsconfig.spec.json b/libs/core/tsconfig.spec.json index e767c47..8d7d1ca 100644 --- a/libs/core/tsconfig.spec.json +++ b/libs/core/tsconfig.spec.json @@ -1,17 +1,22 @@ { - "extends": "./tsconfig.json", + "extends": "../../tsconfig.base.json", "compilerOptions": { - "outDir": "../../dist/out-tsc", - "module": "commonjs", + "outDir": "./out-tsc/jest", "types": [ "jest", "node" - ] + ], + "forceConsistentCasingInFileNames": true }, "include": [ "jest.config.ts", - "src/**/*.test.ts", - "src/**/*.spec.ts", - "src/**/*.d.ts" + "jest.config.cts", + "src/**/*.d.ts", + "test/**/*.ts" + ], + "references": [ + { + "path": "./tsconfig.lib.json" + } ] -} \ No newline at end of file +} diff --git a/nx.json b/nx.json index a5ad526..5c82a41 100644 --- a/nx.json +++ b/nx.json @@ -1,42 +1,6 @@ { "$schema": "./node_modules/nx/schemas/nx-schema.json", - "targetDefaults": { - "build": { - "dependsOn": [ - "^build" - ], - "inputs": [ - "production", - "^production" - ], - "cache": true - }, - "lint": { - "inputs": [ - "default", - "{workspaceRoot}/.eslintrc.cjs", - "{workspaceRoot}/.eslintignore" - ], - "cache": true - }, - "@nx/jest:jest": { - "inputs": [ - "default", - "^production", - "{workspaceRoot}/jest.preset.js" - ], - "cache": true, - "options": { - "passWithNoTests": true - }, - "configurations": { - "ci": { - "ci": true, - "codeCoverage": true - } - } - } - }, + "analytics": false, "namedInputs": { "default": [ "{projectRoot}/**/*", @@ -44,13 +8,63 @@ ], "production": [ "default", + "!{projectRoot}/.eslintrc.json", + "!{projectRoot}/eslint.config.mjs", "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)", "!{projectRoot}/tsconfig.spec.json", "!{projectRoot}/jest.config.[jt]s", - "!{projectRoot}/.eslintrc.cjs", - "!{projectRoot}/src/test-setup.[jt]s" + "!{projectRoot}/src/test-setup.[jt]s", + "!{projectRoot}/test-setup.[jt]s" ], "sharedGlobals": [] }, - "tui": { "enabled": false } + "plugins": [ + { + "plugin": "@nx/js/typescript", + "exclude": [ + "legacy/**/*" + ], + "options": { + "typecheck": { + "targetName": "typecheck" + }, + "build": { + "targetName": "build", + "configName": "tsconfig.lib.json", + "buildDepsName": "build-deps", + "watchDepsName": "watch-deps" + } + } + }, + { + "plugin": "@nx/webpack/plugin", + "options": { + "buildTargetName": "build", + "serveTargetName": "serve", + "previewTargetName": "preview", + "buildDepsTargetName": "build-deps", + "watchDepsTargetName": "watch-deps", + "serveStaticTargetName": "serve-static" + } + }, + { + "plugin": "@nx/eslint/plugin", + "options": { + "targetName": "lint" + } + }, + { + "plugin": "@nx/jest/plugin", + "options": { + "targetName": "test" + } + } + ], + "targetDefaults": { + "test": { + "dependsOn": [ + "^build" + ] + } + } } diff --git a/package.json b/package.json index b41a10c..3b0fc6e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nxt-backend", - "version": "1.1.2", + "version": "0.0.0", "license": "MPL-2.0", "authors": [ { @@ -12,119 +12,54 @@ "email": "tommaso.girotto91@gmail.com" } ], + "packageManager": "pnpm@11.12.0", + "engines": { + "node": "24.x" + }, "scripts": { - "check-types": "tsc -p tsconfig.base.json --noEmit --allowSyntheticDefaultImports", - "eslint": "eslint . --ext .ts", - "eslint:fix": "eslint . --ext .ts --fix", - "lint": "npm run eslint && npm run check-types", "prepare": "husky", - "fix-node-cpu": "npm rebuild fsevents", - "build:tiamat": "nx build tiamat", - "preinstall": "npx npm-force-resolutions", - "build:loch": "nx build loch", - "build:yeti": "nx build yeti", - "gen-types-remote": "npx supabase@latest gen types typescript --project-id axenumkepgnwfmdogkqq > supabase/generated-types.ts", - "gen-types-local": "npx supabase@latest gen types typescript --local > supabase/generated-types.ts", - "gen-better-types": "npx better-supabase-types -i supabase/generated-types.ts -o libs/core/src/types/supabase-types.ts -s --enumAsType && node .scripts/fix-supabase-json-type.js && eslint --fix libs/core/src/types/supabase-types.ts && rm supabase/generated-types.ts", - "generate-types": "npm run gen-types-remote && npm run gen-better-types", - "generate-types:local": "npm run gen-types-local && npm run gen-better-types" + "supabase": "supabase", + "gen-types-local": "pnpm supabase gen types typescript --local --schema public > supabase/generated-types.ts", + "gen-better-types": "better-supabase-types -i supabase/generated-types.ts -o libs/core/src/types/supabase-types.ts -s --enumAsType && node supabase/scripts/fix-supabase-json-type.js && eslint --fix libs/core/src/types/supabase-types.ts && rm supabase/generated-types.ts", + "generate-types:local": "pnpm gen-types-local && pnpm gen-better-types" }, "private": true, - "dependencies": { - "@aws-sdk/client-s3": "^3.842.0", - "@chirpstack/chirpstack-api": "^4.13.0", - "@grpc/grpc-js": "^1.13.4", - "@nestjs/axios": "4.0.0", - "@nestjs/common": "11.1.3", - "@nestjs/core": "11.1.3", - "@nestjs/jwt": "11.0.0", - "@nestjs/mapped-types": "^2.1.0", - "@nestjs/passport": "11.0.5", - "@nestjs/platform-express": "11.1.3", - "@nestjs/platform-socket.io": "11.1.3", - "@nestjs/schedule": "6.0.0", - "@nestjs/typeorm": "11.0.0", - "@nestjs/websockets": "11.1.3", - "@sendgrid/mail": "8.1.5", - "@supabase/supabase-js": "^2.103.3", - "africastalking": "^0.7.3", - "async-sema": "^3.1.1", - "class-transformer": "^0.5.1", - "class-validator": "^0.14.2", - "flutterwave-node-v3": "1.3.0", - "form-data": "^4.0.3", - "iovalkey": "^0.3.3", - "lodash.throttle": "^4.1.1", - "moment": "^2.30.1", - "moment-timezone": "^0.6.0", - "mongodb": "^6.17.0", - "mqtt": "^5.13.2", - "passport-headerapikey": "^1.2.2", - "passport-http-bearer": "^1.0.1", - "pg": "^8.16.3", - "puppeteer": "^24.12.0", - "ramda": "^0.31.3", - "recast": "^0.23.11", - "reflect-metadata": "^0.2.2", - "rxjs": "^7.8.2", - "tslib": "^2.8.1", - "typeorm": "^0.3.25", - "typeorm-extension": "^3.7.1", - "ulid": "^3.0.1", - "uuid": "^11.1.0", - "winston": "^3.17.0", - "winston-loki": "^6.1.3" - }, "devDependencies": { - "@eslint/js": "^9.30.1", - "@nestjs/schematics": "11.0.5", - "@nestjs/testing": "11.1.3", - "@nx/eslint": "21.2.2", - "@nx/eslint-plugin": "21.2.2", - "@nx/jest": "21.2.2", - "@nx/js": "21.2.2", - "@nx/nest": "21.2.2", - "@nx/node": "21.2.2", - "@nx/webpack": "21.2.2", - "@nx/workspace": "21.2.2", - "@types/express": "^5.0.3", - "@types/jest": "^30.0.0", - "@types/multer": "^2.0.0", - "@types/node": "~24.0.10", - "@types/passport-http-bearer": "^1.0.41", - "@types/pg": "^8.16.0", - "@types/ramda": "^0.31.1", - "@typescript-eslint/eslint-plugin": "8.36.0", - "@typescript-eslint/parser": "8.36.0", - "dotenv-safe": "^9.1.0", - "eslint": "^9.30.1", - "eslint-plugin-deno": "^0.0.1", - "eslint-plugin-eslint-comments": "^3.2.0", + "@eslint/js": "^9.8.0", + "@nestjs/schematics": "^11.0.0", + "@nx/eslint": "23.0.2", + "@nx/eslint-plugin": "23.0.2", + "@nx/jest": "23.0.2", + "@nx/js": "23.0.2", + "@nx/nest": "23.0.2", + "@nx/node": "23.0.2", + "@nx/web": "23.0.2", + "@nx/webpack": "23.0.2", + "@swc/helpers": "~0.5.18", + "@swc/jest": "~0.2.38", + "@types/jest": "~30.0.0", + "@types/node": "^22.0.0", + "better-supabase-types": "2.7.6", + "eslint": "^9.8.0", + "eslint-config-prettier": "^10.0.0", "husky": "^9.1.7", - "jest": "^30.0.4", - "jest-environment-node": "^30.0.4", - "lint-staged": "^16.1.2", - "npm-force-resolutions": "^0.0.10", - "nx": "21.2.2", - "supabase": "^2.54.10", - "ts-jest": "29.4.0", - "ts-node": "10.9.2", - "typescript": "^5.8.3", - "typescript-eslint": "^8.36.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.3" + "jest": "~30.3.0", + "jest-environment-node": "~30.3.0", + "jest-util": "~30.3.0", + "jsonc-eslint-parser": "^2.1.0", + "lint-staged": "^17.0.8", + "nx": "23.0.2", + "supabase": "2.109.1", + "ts-jest": "^29.4.0", + "ts-node": "10.9.1", + "tslib": "^2.3.0", + "typescript": "~5.9.2", + "typescript-eslint": "^8.40.0", + "webpack": "^5.101.3", + "webpack-cli": "^7.0.0", + "webpack-dev-server": "^5.2.1" }, "lint-staged": { - "{src,apps,libs,test}/**/*.ts": "eslint --max-warnings=0" - }, - "engines": { - "node": "22.x" - }, - "resolutions": { - "**/semver": "^7.7.2", - "**/word-wrap": "^1.2.4", - "**/cookie": "^0.7.0", - "**/validate.js": "^0.13.1" + "**/*.ts": "eslint --max-warnings=0 --no-warn-ignored" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..e07048a --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,13135 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@eslint/js': + specifier: ^9.8.0 + version: 9.39.5 + '@nestjs/schematics': + specifier: ^11.0.0 + version: 11.1.0(chokidar@4.0.3)(prettier@3.9.5)(typescript@5.9.3) + '@nx/eslint': + specifier: 23.0.2 + version: 23.0.2(@babel/traverse@7.29.7)(@nx/jest@23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)))(ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.3.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(typescript@5.9.3))(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3))(typescript@5.9.3))(@swc/core@1.15.43(@swc/helpers@0.5.23))(@zkochan/js-yaml@0.0.7)(eslint@9.39.5(jiti@2.7.0))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))) + '@nx/eslint-plugin': + specifier: 23.0.2 + version: 23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(@typescript-eslint/parser@8.63.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint-config-prettier@10.1.8(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)))(typescript@5.9.3) + '@nx/jest': + specifier: 23.0.2 + version: 23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)))(ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.3.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(typescript@5.9.3))(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3))(typescript@5.9.3) + '@nx/js': + specifier: 23.0.2 + version: 23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))) + '@nx/nest': + specifier: 23.0.2 + version: 23.0.2(d5d1d2d47871c69071379b8e39d48666) + '@nx/node': + specifier: 23.0.2 + version: 23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(@zkochan/js-yaml@0.0.7)(babel-plugin-macros@3.1.0)(eslint@9.39.5(jiti@2.7.0))(express@5.2.1)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)))(ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.3.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(typescript@5.9.3))(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3))(typescript@5.9.3) + '@nx/web': + specifier: 23.0.2 + version: 23.0.2(f62ac14c8923e53a1e4e1648925cde7c) + '@nx/webpack': + specifier: 23.0.2 + version: 23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)))(typescript@5.9.3)(webpack-cli@7.2.1)(webpack-dev-server@5.2.6)(webpack@5.108.4) + '@swc/helpers': + specifier: ~0.5.18 + version: 0.5.23 + '@swc/jest': + specifier: ~0.2.38 + version: 0.2.39(@swc/core@1.15.43(@swc/helpers@0.5.23)) + '@types/jest': + specifier: ~30.0.0 + version: 30.0.0 + '@types/node': + specifier: ^22.0.0 + version: 22.20.1 + better-supabase-types: + specifier: 2.7.6 + version: 2.7.6 + eslint: + specifier: ^9.8.0 + version: 9.39.5(jiti@2.7.0) + eslint-config-prettier: + specifier: ^10.0.0 + version: 10.1.8(eslint@9.39.5(jiti@2.7.0)) + husky: + specifier: ^9.1.7 + version: 9.1.7 + jest: + specifier: ~30.3.0 + version: 30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)) + jest-environment-node: + specifier: ~30.3.0 + version: 30.3.0 + jest-util: + specifier: ~30.3.0 + version: 30.3.0 + jsonc-eslint-parser: + specifier: ^2.1.0 + version: 2.4.2 + lint-staged: + specifier: ^17.0.8 + version: 17.0.8 + nx: + specifier: 23.0.2 + version: 23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)) + supabase: + specifier: 2.109.1 + version: 2.109.1 + ts-jest: + specifier: ^29.4.0 + version: 29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.3.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(typescript@5.9.3) + ts-node: + specifier: 10.9.1 + version: 10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3) + tslib: + specifier: ^2.3.0 + version: 2.8.1 + typescript: + specifier: ~5.9.2 + version: 5.9.3 + typescript-eslint: + specifier: ^8.40.0 + version: 8.63.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3) + webpack: + specifier: ^5.101.3 + version: 5.108.4(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack-cli@7.2.1) + webpack-cli: + specifier: ^7.0.0 + version: 7.2.1(js-yaml@4.3.0)(json5@2.2.3)(webpack-dev-server@5.2.6)(webpack@5.108.4) + webpack-dev-server: + specifier: ^5.2.1 + version: 5.2.6(tslib@2.8.1)(webpack-cli@7.2.1)(webpack@5.108.4) + + apps/api: + dependencies: + '@nestjs/common': + specifier: ^11.0.0 + version: 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': + specifier: ^11.0.0 + version: 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/passport': + specifier: ^11.0.5 + version: 11.0.5(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0) + '@nestjs/platform-express': + specifier: ^11.0.0 + version: 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28) + '@nxt/core': + specifier: workspace:* + version: link:../../libs/core + '@supabase/supabase-js': + specifier: ^2.110.5 + version: 2.110.5 + class-transformer: + specifier: ^0.5.1 + version: 0.5.1 + class-validator: + specifier: ^0.15.1 + version: 0.15.1 + jose: + specifier: ^6.2.3 + version: 6.2.3 + passport: + specifier: ^0.7.0 + version: 0.7.0 + passport-headerapikey: + specifier: ^1.2.2 + version: 1.2.2 + passport-http-bearer: + specifier: ^1.0.1 + version: 1.0.1 + ramda: + specifier: ^0.31.3 + version: 0.31.3 + reflect-metadata: + specifier: ^0.2.0 + version: 0.2.2 + rxjs: + specifier: ^7.8.0 + version: 7.8.2 + tslib: + specifier: ^2.3.0 + version: 2.8.1 + devDependencies: + '@nestjs/testing': + specifier: ^11.0.0 + version: 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(@nestjs/platform-express@11.1.28) + '@types/passport': + specifier: ^1.0.17 + version: 1.0.17 + '@types/passport-http-bearer': + specifier: ^1.0.42 + version: 1.0.42 + '@types/ramda': + specifier: ^0.31.1 + version: 0.31.1 + '@types/supertest': + specifier: ^7.2.1 + version: 7.2.1 + supertest: + specifier: ^7.2.2 + version: 7.2.2 + + apps/worker: + dependencies: + '@nestjs/common': + specifier: ^11.0.0 + version: 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': + specifier: ^11.0.0 + version: 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/platform-express': + specifier: ^11.0.0 + version: 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28) + '@nestjs/schedule': + specifier: ^6.1.3 + version: 6.1.3(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28) + '@nxt/core': + specifier: workspace:* + version: link:../../libs/core + reflect-metadata: + specifier: ^0.2.0 + version: 0.2.2 + rxjs: + specifier: ^7.8.0 + version: 7.8.2 + tslib: + specifier: ^2.3.0 + version: 2.8.1 + devDependencies: + '@nestjs/testing': + specifier: ^11.0.0 + version: 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(@nestjs/platform-express@11.1.28) + + libs/core: + dependencies: + '@nestjs/axios': + specifier: ^4.0.1 + version: 4.0.1(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.18.1)(rxjs@7.8.2) + '@nestjs/common': + specifier: ^11.0.0 + version: 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@supabase/supabase-js': + specifier: ^2.103.3 + version: 2.110.5 + class-validator: + specifier: ^0.15.1 + version: 0.15.1 + tslib: + specifier: ^2.3.0 + version: 2.8.1 + zod: + specifier: ^4.4.3 + version: 4.4.3 + +packages: + + '@angular-devkit/core@19.2.24': + resolution: {integrity: sha512-Kd49warf6U/EyWe5BszF/eebN3zQ3bk7tgfEljAw8q/rX95UUtriJubWvp6pgzHfzBA4jwq8f+QiNZB8eBEXPA==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + peerDependencies: + chokidar: ^4.0.0 + peerDependenciesMeta: + chokidar: + optional: true + + '@angular-devkit/schematics@19.2.24': + resolution: {integrity: sha512-lnw+ZM1Io+cJAkReC0NPDjqObL8NtKzKIkdgEEKC8CUmkhurYhedbicN8Y8NYHgG1uLd2GozW3+/QqPRZaN+Lw==} + engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.7': + resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.29.7': + resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.29.7': + resolution: {integrity: sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.29.7': + resolution: {integrity: sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-define-polyfill-provider@0.6.8': + resolution: {integrity: sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.29.7': + resolution: {integrity: sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.29.7': + resolution: {integrity: sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-remap-async-to-generator@7.29.7': + resolution: {integrity: sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-replace-supers@7.29.7': + resolution: {integrity: sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + resolution: {integrity: sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-wrap-function@7.29.7': + resolution: {integrity: sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7': + resolution: {integrity: sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.29.7': + resolution: {integrity: sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.29.7': + resolution: {integrity: sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.7': + resolution: {integrity: sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.29.7': + resolution: {integrity: sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.29.7': + resolution: {integrity: sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-proposal-decorators@7.29.7': + resolution: {integrity: sha512-EtU0Hi3GvrTqD56xKmZvV/uCXK2ZbwVNPNLAquVItcAZpUhkXwWlo3Fmj0c2LxgSf2I8IDULeAepwNP1OefLXg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-async-generators@7.8.4': + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-bigint@7.8.3': + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-properties@7.12.13': + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-static-block@7.14.5': + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-decorators@7.29.7': + resolution: {integrity: sha512-9MTTLbF39X6sqM92JPEsoI7++26hjZvzkxKZy64aMhWLH2mPkJ/Q3AV4QLmls3R14FpSpkOwQQfUh962JGQxxg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-assertions@7.29.7': + resolution: {integrity: sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.29.7': + resolution: {integrity: sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-meta@7.10.4': + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-json-strings@7.8.3': + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.29.7': + resolution: {integrity: sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4': + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-numeric-separator@7.10.4': + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-object-rest-spread@7.8.3': + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3': + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-chaining@7.8.3': + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-private-property-in-object@7.14.5': + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-top-level-await@7.14.5': + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.29.7': + resolution: {integrity: sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-arrow-functions@7.29.7': + resolution: {integrity: sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-generator-functions@7.29.7': + resolution: {integrity: sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-to-generator@7.29.7': + resolution: {integrity: sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoped-functions@7.29.7': + resolution: {integrity: sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoping@7.29.7': + resolution: {integrity: sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-properties@7.29.7': + resolution: {integrity: sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-static-block@7.29.7': + resolution: {integrity: sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + + '@babel/plugin-transform-classes@7.29.7': + resolution: {integrity: sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-computed-properties@7.29.7': + resolution: {integrity: sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-destructuring@7.29.7': + resolution: {integrity: sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-dotall-regex@7.29.7': + resolution: {integrity: sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-keys@7.29.7': + resolution: {integrity: sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.7': + resolution: {integrity: sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-dynamic-import@7.29.7': + resolution: {integrity: sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-explicit-resource-management@7.29.7': + resolution: {integrity: sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-exponentiation-operator@7.29.7': + resolution: {integrity: sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-export-namespace-from@7.29.7': + resolution: {integrity: sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-for-of@7.29.7': + resolution: {integrity: sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-function-name@7.29.7': + resolution: {integrity: sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-json-strings@7.29.7': + resolution: {integrity: sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-literals@7.29.7': + resolution: {integrity: sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-logical-assignment-operators@7.29.7': + resolution: {integrity: sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-member-expression-literals@7.29.7': + resolution: {integrity: sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-amd@7.29.7': + resolution: {integrity: sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.29.7': + resolution: {integrity: sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-systemjs@7.29.7': + resolution: {integrity: sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-umd@7.29.7': + resolution: {integrity: sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.7': + resolution: {integrity: sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-new-target@7.29.7': + resolution: {integrity: sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-nullish-coalescing-operator@7.29.7': + resolution: {integrity: sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-numeric-separator@7.29.7': + resolution: {integrity: sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-rest-spread@7.29.7': + resolution: {integrity: sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-super@7.29.7': + resolution: {integrity: sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-catch-binding@7.29.7': + resolution: {integrity: sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.29.7': + resolution: {integrity: sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.29.7': + resolution: {integrity: sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-methods@7.29.7': + resolution: {integrity: sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-property-in-object@7.29.7': + resolution: {integrity: sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-property-literals@7.29.7': + resolution: {integrity: sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.29.7': + resolution: {integrity: sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regexp-modifiers@7.29.7': + resolution: {integrity: sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-reserved-words@7.29.7': + resolution: {integrity: sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-runtime@7.29.7': + resolution: {integrity: sha512-xmAscdE/AsqRW7vutbPNoUmu/nF5SrLKPs7aoJgEjo35lLKA/Bc0i2rMv/hr1+Y0o1bQCiVtith3u2vdgRL39Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.29.7': + resolution: {integrity: sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-spread@7.29.7': + resolution: {integrity: sha512-/u5K1QWada7tbYNqTjMh96718g9NTwh9tfPJMsSmVsQwGT447FskV+KcfeXkXq2GWki4EM/MuTdmBec+hOuVTQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-sticky-regex@7.29.7': + resolution: {integrity: sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-template-literals@7.29.7': + resolution: {integrity: sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typeof-symbol@7.29.7': + resolution: {integrity: sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.29.7': + resolution: {integrity: sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-escapes@7.29.7': + resolution: {integrity: sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-property-regex@7.29.7': + resolution: {integrity: sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-regex@7.29.7': + resolution: {integrity: sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-sets-regex@7.29.7': + resolution: {integrity: sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/preset-env@7.29.7': + resolution: {integrity: sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-modules@0.1.6-no-external-plugins': + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + + '@babel/preset-typescript@7.29.7': + resolution: {integrity: sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.7': + resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@0.2.3': + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + + '@borewit/text-codec@0.2.2': + resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==} + + '@bufbuild/protobuf@2.12.1': + resolution: {integrity: sha512-BvAMfS6LrgZiryOAZ4pBYucu4wG/Ei/9o9DZ9akbREnMLbPJiom2i8b9C8IsKErQoiKqVhrerzt3kOT/RrzLHg==} + + '@colordx/core@5.5.0': + resolution: {integrity: sha512-3PxTH8itZzltK0U9jTwVVnjLXvnDYuq3m+QXsHkENxWiPRh4WaoLcs1SQjqgZ55kS+QyirpH5BVwzP2gMVG6EQ==} + + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + + '@discoveryjs/json-ext@1.1.0': + resolution: {integrity: sha512-Xc3VhU02wqZ1HvHRJUwL09HkZSTvidqY5Ya0NXBSYOxAp+Ln9dcJr9fySI+CkONzP3PekQo9WdzCv0PGER/mOA==} + engines: {node: '>=14.17.0'} + + '@ecies/ciphers@0.2.6': + resolution: {integrity: sha512-patgsRPKGkhhoBjETV4XxD0En4ui5fbX0hzayqI3M8tvNMGUoUvmyYAIWwlxBc1KX5cturfqByYdj5bYGRpN9g==} + engines: {bun: '>=1', deno: '>=2.7.10', node: '>=16'} + peerDependencies: + '@noble/ciphers': ^1.0.0 + + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + + '@emnapi/core@1.4.5': + resolution: {integrity: sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==} + + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + + '@emnapi/runtime@1.4.5': + resolution: {integrity: sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==} + + '@emnapi/wasi-threads@1.0.4': + resolution: {integrity: sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==} + + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.6': + resolution: {integrity: sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.5': + resolution: {integrity: sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@istanbuljs/load-nyc-config@1.1.0': + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + + '@istanbuljs/schema@0.1.6': + resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==} + engines: {node: '>=8'} + + '@jest/console@30.3.0': + resolution: {integrity: sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/console@30.4.1': + resolution: {integrity: sha512-v3bhyxUh9Hgmo5p6hAOXe14/R3ZxZDOsvHleh4B07z3m/x4/ngPUXEm9XwK4sF4u+f+P2ORb0Ge+MgpaqRMVDA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/core@30.3.0': + resolution: {integrity: sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/create-cache-key-function@30.4.1': + resolution: {integrity: sha512-R+xGEtzA95NIsvpXJSROG4t01956dDOt17KpamguY4XOnGvdHNFFXE7Er0C1OAsRjOwiIxpKqOvGlznIGZIQlQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/diff-sequences@30.0.1': + resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/diff-sequences@30.3.0': + resolution: {integrity: sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/diff-sequences@30.4.0': + resolution: {integrity: sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/environment@30.3.0': + resolution: {integrity: sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/environment@30.4.1': + resolution: {integrity: sha512-AK9yNRqgKxiabqMoe4oW+3/TSSeV8vkdC7BGaxZdU0AFXfOpofTLqdru2GXKZghP3sdgwE9XXpnVwfZ8JnFV4w==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/expect-utils@30.3.0': + resolution: {integrity: sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/expect-utils@30.4.1': + resolution: {integrity: sha512-ZBn5CglH8fBsQsvs4VWNzD4aWfUYks+IdOOQU3MEK71ol/BcVm+P+rtb1KpiFBpSWSCE27uOahyyf1vfqOVbcQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/expect@30.3.0': + resolution: {integrity: sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/expect@30.4.1': + resolution: {integrity: sha512-ginrj6TMgh2GshLUGCjO94Ptx9HhdZA/I6A9iUfyeLKFtdAjnKzHDgzgP9HYQgbxM1lbXScQ2eUBz2lGeVDPWA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/fake-timers@30.3.0': + resolution: {integrity: sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/fake-timers@30.4.1': + resolution: {integrity: sha512-iW5umdmfPeWzehrVhugFQZqCchSCud5S1l2YT0O9ZhjRR0ExclANDZkiSBwzqtnlOn0J1JXvO+HZ6rkuyOVOgQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/get-type@30.1.0': + resolution: {integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/globals@30.3.0': + resolution: {integrity: sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/globals@30.4.1': + resolution: {integrity: sha512-ZbuY4cmXC8DkxYjfvT2DbcHWL2T6vmsMhXCDcmTB2T0y0gaezBI77ufq5ZAIdcRkYZ7NEQEDg1xFeKbxUJ5v5Q==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/pattern@30.0.1': + resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/pattern@30.4.0': + resolution: {integrity: sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/reporters@30.3.0': + resolution: {integrity: sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/reporters@30.4.1': + resolution: {integrity: sha512-/SnkPCzEQpUaBH81kjdEdDdo2WZl5hxw+BmLDGWjRkm8o7XlhjwsU36cqwe5PGBE5WYpBvDzRSdXx9rbGuJtNA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/schemas@30.0.5': + resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/schemas@30.4.1': + resolution: {integrity: sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/snapshot-utils@30.3.0': + resolution: {integrity: sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/snapshot-utils@30.4.1': + resolution: {integrity: sha512-ObY4ljvQ95mt6iwKtVLetR/4yXiAgl3H4nJxhztr0MTjrN97TwDYrnCp/kF60Ec9HdhkWTHSu+Hg05aXfngpOA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/source-map@30.0.1': + resolution: {integrity: sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/test-result@30.3.0': + resolution: {integrity: sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/test-result@30.4.1': + resolution: {integrity: sha512-/ZG7pgEiOmmWkN9TplKbOu4id2N5lh7FHwRwlkgBVAzGdRH+OkkQ8wX/kIxg4zmd3ZQvAL1RwL2yWsvNYYECTw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/test-sequencer@30.3.0': + resolution: {integrity: sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/test-sequencer@30.4.1': + resolution: {integrity: sha512-PeYE+4td5rKjoRPxztObrXU+H8hsjZfxKMXOcmrr34JerSyB/ROOxbbicz8B7A5j9R9VayDnVPvBmedqCsFCdw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/transform@30.3.0': + resolution: {integrity: sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/transform@30.4.1': + resolution: {integrity: sha512-Wz0LyktlTvRefoymh+n64hQ84KNXsRGcwdoZ8CSa0Ea+fgYcHZlnk+hDP7v2MS7il2bQ5uTEIxf4/NNfhMN4KQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/types@30.3.0': + resolution: {integrity: sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/types@30.4.1': + resolution: {integrity: sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + + '@jsonjoy.com/base64@1.1.2': + resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/base64@17.67.0': + resolution: {integrity: sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/buffers@1.2.1': + resolution: {integrity: sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/buffers@17.67.0': + resolution: {integrity: sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/codegen@1.0.0': + resolution: {integrity: sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/codegen@17.67.0': + resolution: {integrity: sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-core@4.64.0': + resolution: {integrity: sha512-zs2TAq7Six5jgMuoMNjpspAvOP3mhtgq/k1UyQodEzCtQi/N83y2/y+zcvnZSGp/Rxq96DBN+bValOBQAyn/ew==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-fsa@4.64.0': + resolution: {integrity: sha512-nMWOVbkLFyEgmXZih3wyvxA9XpgyyqyfrINMHvEFqhi7uqfRl7c9ERJt6yX7vgMPrB9Uo+OJO+Spa0cFzPD01w==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node-builtins@4.64.0': + resolution: {integrity: sha512-/o7WRFhUWaM/fOrslwLZGnzn4RmRILykn+lAL+mNObqqRNw+CQSiij6hpCeZ+C7buhdoVo7go/OYqzaSUfDYmA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node-to-fsa@4.64.0': + resolution: {integrity: sha512-WDD9WVs0hb7UAEKTgZW2f66WDrbj7gIIWwpP3spbLyXa0rghtUaFTB8L4gdR3ZCWwiKIsj38/CNijpVmpnuPUw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node-utils@4.64.0': + resolution: {integrity: sha512-k5Indsx9hWW9xSF7Y6oSKKwtCUNhzZxadub3owhIlitc+iMRVlPPdX2duTKQWBL3qNWpXya8jykgaaWpheeS4w==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node@4.64.0': + resolution: {integrity: sha512-dO+NNkODbUli4uV42bcNrrLvq5rE7SNpdZ5TNd0dtbLsAaNK3MDiIC9lUi+brboGoIjW6vd2fB1qao60nrk5xA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-print@4.64.0': + resolution: {integrity: sha512-PHZFccchvkhWrwPWHjmVAhbC3vSHCtyZvlZfJJ3ho2bnzl450hXri6/8e6pbkWdH+SkmLXNml0sV8e5HDAfxKw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-snapshot@4.64.0': + resolution: {integrity: sha512-oM7UDeL83q6NBzzsfKAsYKXKVXlykKFqqOLh4xZZKAzzROTlInkPbc6LTDGThEOnPiFiUzA7tYziHG9xavd76Q==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pack@1.21.0': + resolution: {integrity: sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pack@17.67.0': + resolution: {integrity: sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pointer@1.0.2': + resolution: {integrity: sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pointer@17.67.0': + resolution: {integrity: sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/util@1.9.0': + resolution: {integrity: sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/util@17.67.0': + resolution: {integrity: sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@leichtgewicht/ip-codec@2.0.5': + resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} + + '@lukeed/csprng@1.1.0': + resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} + engines: {node: '>=8'} + + '@napi-rs/wasm-runtime@0.2.4': + resolution: {integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==} + + '@napi-rs/wasm-runtime@1.1.6': + resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + + '@nestjs/axios@4.0.1': + resolution: {integrity: sha512-68pFJgu+/AZbWkGu65Z3r55bTsCPlgyKaV4BSG8yUAD72q1PPuyVRgUwFv6BxdnibTUHlyxm06FmYWNC+bjN7A==} + peerDependencies: + '@nestjs/common': ^10.0.0 || ^11.0.0 + axios: ^1.3.1 + rxjs: ^7.0.0 + + '@nestjs/common@11.1.28': + resolution: {integrity: sha512-bRImsxibie+AM7xjdwcrm/gr5YeacI65kSBNzTufa1Ib5iwziaY/lqMtRh9THq6pbV4e1HP9aI2ZxGUumnmaoQ==} + peerDependencies: + class-transformer: '>=0.4.1' + class-validator: '>=0.13.2' + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + + '@nestjs/core@11.1.28': + resolution: {integrity: sha512-06m63xIRj8+l8uOeh/8LnYupGubkyu4f+bPKIadaSui6vK9KpXgoz7HveT1yOVLcEt0M0oCOEW5EuEXZkEmBBQ==} + engines: {node: '>= 20'} + peerDependencies: + '@nestjs/common': ^11.0.0 + '@nestjs/microservices': ^11.0.0 + '@nestjs/platform-express': ^11.0.0 + '@nestjs/websockets': ^11.0.0 + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + '@nestjs/microservices': + optional: true + '@nestjs/platform-express': + optional: true + '@nestjs/websockets': + optional: true + + '@nestjs/passport@11.0.5': + resolution: {integrity: sha512-ulQX6mbjlws92PIM15Naes4F4p2JoxGnIJuUsdXQPT+Oo2sqQmENEZXM7eYuimocfHnKlcfZOuyzbA33LwUlOQ==} + peerDependencies: + '@nestjs/common': ^10.0.0 || ^11.0.0 + passport: ^0.5.0 || ^0.6.0 || ^0.7.0 + + '@nestjs/platform-express@11.1.28': + resolution: {integrity: sha512-hU+9Sz4m+onHrR5AmelI59QKmY/Re546bPnygnpqqeQdHDiJpBgjWbL4t6Jr73CBpS60cpyng7WzjgphNB9iwA==} + peerDependencies: + '@nestjs/common': ^11.0.0 + '@nestjs/core': ^11.0.0 + + '@nestjs/schedule@6.1.3': + resolution: {integrity: sha512-RflMFOpR16Dwd1jAUbeB4mfGTCh65fvEdL4mSjQPJChpkRGRjIXjb+6YQcK2faQrVT60c9DmLmoVR7/ONCtuYQ==} + peerDependencies: + '@nestjs/common': ^10.0.0 || ^11.0.0 + '@nestjs/core': ^10.0.0 || ^11.0.0 + + '@nestjs/schematics@11.1.0': + resolution: {integrity: sha512-lVxGZ46tcdItFMoXr6vyKWlnOsm1SZm/GUqAEDvy2RL4Q4O+3bkziAhrO7Y8JLssFUUvNFEGqAizI52WAxhjDw==} + peerDependencies: + prettier: ^3.0.0 + typescript: '>=4.8.2' + peerDependenciesMeta: + prettier: + optional: true + + '@nestjs/testing@11.1.28': + resolution: {integrity: sha512-B+VgRxeLaH7jkOMgAyUP3N3rpFlisQ7JRxixRbgHvG6a0VgKbbkNSofKExexCgKmQQak80undb3+2kE1lUBmRQ==} + peerDependencies: + '@nestjs/common': ^11.0.0 + '@nestjs/core': ^11.0.0 + '@nestjs/microservices': ^11.0.0 + '@nestjs/platform-express': ^11.0.0 + peerDependenciesMeta: + '@nestjs/microservices': + optional: true + '@nestjs/platform-express': + optional: true + + '@noble/ciphers@1.3.0': + resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.7': + resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.4.0': + resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} + engines: {node: '>= 16'} + + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@nx/devkit@23.0.2': + resolution: {integrity: sha512-svkaufC+nucp3stMucqVN0sV4nz9fEgAd33WB9u2fnFWYx/4F0VJcV4ZLyiG3hHrgs2/Gz5K0OnygPqjCsSqMw==} + peerDependencies: + nx: '>= 22 <= 24 || ^23.0.0-0' + + '@nx/docker@23.0.2': + resolution: {integrity: sha512-3eccPjL3kwAndVq7t0V1XQDEkEGz7AhnSzpPwr9IMHgVKqgCIJBbNF1Xi5c3AbPlnjy0GHfnGOeb1/jXf2RuNA==} + + '@nx/eslint-plugin@23.0.2': + resolution: {integrity: sha512-ckkifbXqmYrA7OgpfxqeqG5feYndN28hfvbMA0yD0nWQFbx24lC6CkzsJJjuIN3RsQa8yrhKIoObYtiMRxdNLQ==} + peerDependencies: + '@typescript-eslint/parser': ^8.0.0 + eslint-config-prettier: ^10.0.0 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint-config-prettier: + optional: true + + '@nx/eslint@23.0.2': + resolution: {integrity: sha512-nghKUrUWPj6mZajGkPIC1LxSO+GYWifZjtFnEQU7n5SDffS+QA3mUX5Tr16CZL5tcSLTwpk2/fx3GM/Ji/3DOA==} + peerDependencies: + '@nx/jest': 23.0.2 + '@zkochan/js-yaml': 0.0.7 + eslint: ^8.0.0 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + '@nx/jest': + optional: true + '@zkochan/js-yaml': + optional: true + + '@nx/jest@23.0.2': + resolution: {integrity: sha512-NGMUJMzEccj0y/yk+ct+OIFNB1RrdC9C6rFBZ3VFJgxbJlSbxCqQXcO+/668CtGlLVPAZe0baASrCVcvKNUGCQ==} + peerDependencies: + jest: ^29.0.0 || ^30.0.0 + ts-jest: ^29.0.0 + peerDependenciesMeta: + jest: + optional: true + ts-jest: + optional: true + + '@nx/js@23.0.2': + resolution: {integrity: sha512-ixtH09vcr6qFaYloCC8JOMLazxTocws2FnHWNbLOpYaO0Nyh4szh8Ttclky7eiI21XYF9z07+0O7/GFlh8hCBA==} + peerDependencies: + '@swc/cli': '>=0.6.0 <0.9.0' + verdaccio: ^6.0.5 + peerDependenciesMeta: + '@swc/cli': + optional: true + verdaccio: + optional: true + + '@nx/nest@23.0.2': + resolution: {integrity: sha512-HwawKHpIImnwNfLyULeE+AywB/BIVm9m+ZuoulYP8Gwjvg7Lrq28stnf98sTKEuyGjDDWvTWH4RNHYs0nRXEiA==} + peerDependencies: + '@nestjs/common': '>=10.0.0 <12.0.0' + '@nestjs/core': '>=10.0.0 <12.0.0' + reflect-metadata: '>=0.1.0 <0.3.0' + rxjs: ^7.0.0 + peerDependenciesMeta: + '@nestjs/common': + optional: true + '@nestjs/core': + optional: true + reflect-metadata: + optional: true + rxjs: + optional: true + + '@nx/node@23.0.2': + resolution: {integrity: sha512-WugI9xFXFs0GEKaelYDl+kpbPgwtvleSI1gnCxsA8MukjqZlm5cSTB4/+fOQSFaWhTesR0BZW5SasRQBJTB65A==} + peerDependencies: + express: '>=4.0.0 <6.0.0' + fastify: '>=4.0.0 <6.0.0' + koa: '>=2.0.0 <4.0.0' + peerDependenciesMeta: + express: + optional: true + fastify: + optional: true + koa: + optional: true + + '@nx/nx-darwin-arm64@23.0.2': + resolution: {integrity: sha512-9sqhZMVFpF+qM7hq6y2xA4gVK+6RdxRioAwHxorhOZRSXdW7Y7NESs5fm8vOmdddlG07QB7sMefOKLrqCV3zGg==} + cpu: [arm64] + os: [darwin] + + '@nx/nx-darwin-x64@23.0.2': + resolution: {integrity: sha512-p6L3AvRhRRaR8Bl3jr76/9H04RdWUQbSgB7agK7GB7vqaLI8RifP2lqeaXcAngzjDAjw2EAf0TjOBP+T67hhcg==} + cpu: [x64] + os: [darwin] + + '@nx/nx-freebsd-x64@23.0.2': + resolution: {integrity: sha512-/py4I8Rp2UURses9H/+SQmgPVnHVSJgPimJLhXIfsRavKGu4RS7Ddu1OyNqSkCT3Otic6ImMTtkufURW22KiEQ==} + cpu: [x64] + os: [freebsd] + + '@nx/nx-linux-arm-gnueabihf@23.0.2': + resolution: {integrity: sha512-xv2IzeiWJFWi4WjK0ocMkP+ze1lDeoPVCg0xOTqVs40gM66V1wVw3EK077gTqU4m0Bq1wUxe6/I8WaIGlkLgug==} + cpu: [arm] + os: [linux] + + '@nx/nx-linux-arm64-gnu@23.0.2': + resolution: {integrity: sha512-ckA6hTXST+agxt/HzPGqMss9qFCZhO9b07o8usygb7QFBYQRXFgcYzhTblq4yiTL5ibJmXAGGh98011fLA2MVw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@nx/nx-linux-arm64-musl@23.0.2': + resolution: {integrity: sha512-PAxBxy7m//cKxUeIb6Sk2X5MJ/wjcJcqCx7/L0p8omTt/y/+q1TGpVy6qmJMPUWzNgAULXtsVRSOK4rmiBcrQQ==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@nx/nx-linux-x64-gnu@23.0.2': + resolution: {integrity: sha512-gR146Mo+BjhrIU2fNOJeVjX8HEAHrtmc7IyrD0qD9yqyq0l9Mdx92JnMW3yVQaYIMpPJIqsOvHTDIUbTLFrmTA==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@nx/nx-linux-x64-musl@23.0.2': + resolution: {integrity: sha512-L7JkaoAI0p+DpAi2CNCqPq8nHWkApQw2td0Cyt+stOx/OJlmEteyc/MXyTHBoMPoopNj1wWANJJQm/G1anOLhQ==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@nx/nx-win32-arm64-msvc@23.0.2': + resolution: {integrity: sha512-B3ePaYeu31ivP3i72Vou5RBYFVrDKCMZgz7Jsax+RvEJa8dNfI+Ynh/PSoXzHudN0YsrekkKbjlxNvp/D6fWFA==} + cpu: [arm64] + os: [win32] + + '@nx/nx-win32-x64-msvc@23.0.2': + resolution: {integrity: sha512-/NiB9w8nYrw7LUkcmwAJ9wis5O+kh3ahSZXMDHVYgFnD8yN7kLql39MNJD+/DGstjNSDvWh45ftqr7mZMi6wpQ==} + cpu: [x64] + os: [win32] + + '@nx/web@23.0.2': + resolution: {integrity: sha512-PIUMDB79yvt6nj9ypLTCYZfA7N9v8mvWEHRbt43rusrzl3P4wqZIiveuyIstmt4HuvIHW3rRjbZTSs/eYrkqRg==} + peerDependencies: + '@nx/cypress': 23.0.2 + '@nx/eslint': 23.0.2 + '@nx/jest': 23.0.2 + '@nx/playwright': 23.0.2 + '@nx/vite': 23.0.2 + '@nx/webpack': 23.0.2 + peerDependenciesMeta: + '@nx/cypress': + optional: true + '@nx/eslint': + optional: true + '@nx/jest': + optional: true + '@nx/playwright': + optional: true + '@nx/vite': + optional: true + '@nx/webpack': + optional: true + + '@nx/webpack@23.0.2': + resolution: {integrity: sha512-TbMBG8mLaD7SImTNKH9CZyhgciS6vJZ6/dPK01aHJ4lamKtnFyRGsktgPkLvhKH9jJqLSWSWb4IWAsoHMe46GA==} + peerDependencies: + webpack: ^5.0.0 + webpack-cli: ^5.0.0 || ^6.0.0 || ^7.0.0 + webpack-dev-server: ^5.0.0 + peerDependenciesMeta: + webpack: + optional: true + webpack-cli: + optional: true + webpack-dev-server: + optional: true + + '@nx/workspace@23.0.2': + resolution: {integrity: sha512-7As52HT7l7ypwukR0cKZIDZ771O0hYTDwkfJmzWPHl3YghiROnK3/Xlh/84ITo+4gtCMORUyZXZgQfOi8136ug==} + + '@paralleldrive/cuid2@2.3.1': + resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==} + + '@parcel/watcher-android-arm64@2.5.6': + resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.6': + resolution: {integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.6': + resolution: {integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.6': + resolution: {integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.6': + resolution: {integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm-musl@2.5.6': + resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-arm64-glibc@2.5.6': + resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm64-musl@2.5.6': + resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-x64-glibc@2.5.6': + resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-x64-musl@2.5.6': + resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@parcel/watcher-win32-arm64@2.5.6': + resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.6': + resolution: {integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.6': + resolution: {integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.6': + resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==} + engines: {node: '>= 10.0.0'} + + '@peculiar/asn1-cms@2.8.0': + resolution: {integrity: sha512-NgekZOrSJFSBFLFoLfwePguAWAx7z1+f2TEsWFUMyiqqfntZ4+S/S5hzqME3q4pCA0iOsFKdwiQ35dwY24eVqA==} + + '@peculiar/asn1-csr@2.8.0': + resolution: {integrity: sha512-akbF8+uvleHs8sejNPQxwmVFuInAg6FMNHOwMILXfP518YfFJwdR3jr6oNUPOaEJfuEhn/vkNOCIT6ASUd4mbg==} + + '@peculiar/asn1-ecc@2.8.0': + resolution: {integrity: sha512-ohwlk+u9Rv2NOAY1c6MfHj45ATVF8R1DUN/WCgABiRtLi2ZftlZWZX7KvpAbU8v9xPcmoILfELeEABj/rn18AQ==} + + '@peculiar/asn1-pfx@2.8.0': + resolution: {integrity: sha512-5yof1ytoB++RQtaFbqSUJ8pxDJtZT6vbVqZ8XoJ61ph7UjNVvfFwAilnCodqkNsAodpy13gDhoxZXw00pghnyg==} + + '@peculiar/asn1-pkcs8@2.8.0': + resolution: {integrity: sha512-qAKXtLpBEw9LqhKpjw3ajZSXlBur+ipW+y2ivVBQAG6F6qRx94yO+1ZR4mvw+YaCfKSaOzLeYEzsPaBp4SJELA==} + + '@peculiar/asn1-pkcs9@2.8.0': + resolution: {integrity: sha512-b5nDWCnkV60+cQ141D6sVVwK9nz64R5n3zSVnklGd+ECdkW2Ol3U1a6yYFlalpSOaD557yuJB64A+q42jG7lUQ==} + + '@peculiar/asn1-rsa@2.8.0': + resolution: {integrity: sha512-zHEUlCqB2mk7x2lxDwHHJy7hWZOPdGHVlsmITWKB5/PbQo61atbu9PJ/0r9dQNMwFzbKPXZ8uK8/91eUhRznSg==} + + '@peculiar/asn1-schema@2.8.0': + resolution: {integrity: sha512-7YT0U/ze0tF2QOBbE15gKZwy5tvgGyLRiRHLzhlbOpf7BT032oBSd0haZqXn5W6l26WLlu3dyxzjM+2638/z2Q==} + + '@peculiar/asn1-x509-attr@2.8.0': + resolution: {integrity: sha512-tHjkfS/qhMnmrlB2J9NhflQlQ7In3khO3CfmVrriOlpTeErY9ZIKOso1hQ5JQiyrJ7ShvqVPk7E5fQmbclkSKA==} + + '@peculiar/asn1-x509@2.8.0': + resolution: {integrity: sha512-N0CMuhWUzsWEVq6F1q9X6+VKUnWzSW+cSVg+aPaGGwDdbFoFWTYgin5MHwXgpWd6y9COMBxnfy/Qc+Xc7F0Zwg==} + + '@peculiar/utils@2.0.3': + resolution: {integrity: sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==} + + '@peculiar/x509@1.14.3': + resolution: {integrity: sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==} + engines: {node: '>=20.0.0'} + + '@phenomnomnominal/tsquery@6.2.0': + resolution: {integrity: sha512-Vo9nkhfZxDB/sBiqIY3pjDC4mOSyure+AFlEW5hcy/tRE82MqCXjRN4InnVNMldinRt0dLYqg4HAU2XPq5e1LA==} + peerDependencies: + typescript: '>3.0.0' + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@pkgr/core@0.3.6': + resolution: {integrity: sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==} + engines: {node: ^14.18.0 || >=16.0.0} + + '@sinclair/typebox@0.34.52': + resolution: {integrity: sha512-XiMQh7qqVlxZzcVD+kkGMNGMzcTrDMLWI7S4x7z1MkCkbDPrekpZXEUK0eZqZFMuHQg2a2DZOcDIh9o5v3Gonw==} + + '@sinonjs/commons@3.0.1': + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + + '@sinonjs/fake-timers@15.4.0': + resolution: {integrity: sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==} + + '@supabase/auth-js@2.110.5': + resolution: {integrity: sha512-QSlI5CNeEefHP95/GbeMhNgr8aHEHiXFh8c1IWthYyI9ZzBwEigYzJFCw4Ff+GkL8OK9tArBmGGv6rpg5zLXSw==} + engines: {node: '>=22.0.0'} + + '@supabase/cli-darwin-arm64@2.109.1': + resolution: {integrity: sha512-tkn8tfunyqIL7RE+7DVjg6Ql2cJLPkGgh9cPafp2LbXI0qDgds0TaS+UOTHQEjci8JQXXe2wS00+122ko2QI8A==} + cpu: [arm64] + os: [darwin] + + '@supabase/cli-darwin-x64@2.109.1': + resolution: {integrity: sha512-0Q5ZAoWhOyIv4ZzQOU8QbjjdB2JmznnDNyJ3VrIeuLMWoifVfUWaLZhHBNYzr4xXoxBpKrggomuAT8BaEhY3lg==} + cpu: [x64] + os: [darwin] + + '@supabase/cli-linux-arm64-musl@2.109.1': + resolution: {integrity: sha512-cXNOsSU7MS+jmslGQATTD4DfWBEIHiFi7LaBWyBxHmR7tzIiZXimUXgN26ZiQjfAxU+RZq52C3k0qhi7vmqXQw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@supabase/cli-linux-arm64@2.109.1': + resolution: {integrity: sha512-MS1djJjq5laD99+jYJUoARfSZhzRX9aXmo5piZ1yl2JXb9IXTuhiTD5olkFKQcgNckRcAZqMf4fucQGTYC767A==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@supabase/cli-linux-x64-musl@2.109.1': + resolution: {integrity: sha512-zeD9MrZpEKJrjkSPcYp4nZeGJ9FQt0i/kiRij4ZprPP6R5l+SLi6Pk20ln+Vj/GZuWTVxX7IHJ11pgViaReAWw==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@supabase/cli-linux-x64@2.109.1': + resolution: {integrity: sha512-svFmamF/vIq4/oinwY50jDi869itC9/GWrPaGtsHFkK4NUBcQtl1T37WWIivGsXwbBKNC4FjZD3dGqjL7bfW1g==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@supabase/cli-windows-arm64@2.109.1': + resolution: {integrity: sha512-NeKzgWAOpglnLwMTggTp5t44fbkfxACLkQNlCRx0q332HMM3WqsKQUsHv1MHc6ZbEc5bcMwaYjqPNI/aOWnP5g==} + cpu: [arm64] + os: [win32] + + '@supabase/cli-windows-x64@2.109.1': + resolution: {integrity: sha512-L9/pDLM+4IR8646aT69ZDVcnJeg1lZZsB26ZgI775S3f8jOHP41+1UH9Oq1g9CvKiAQviuaLgtwkuvi0I/cc/A==} + cpu: [x64] + os: [win32] + + '@supabase/functions-js@2.110.5': + resolution: {integrity: sha512-nuQuoIoEGT8ukrwr6THlY5v50bSMJ2rqti1FFsGyDaC98POLmcFQVFFh23PVPhRsKWUxrFc0MpmEoHKa7CY4mg==} + engines: {node: '>=22.0.0'} + + '@supabase/phoenix@0.4.4': + resolution: {integrity: sha512-Gt0pqoXuIqX/8dvG0OKp/wMCobXNH3klNbUPBNyOfN0YA1IswrM3HyWFMOPk1Jy+BRaIyDPcFx4jLBwHNmlyfQ==} + + '@supabase/postgrest-js@2.110.5': + resolution: {integrity: sha512-eObfgBjxLPzFakwMpUmsv8nuNPOhoDhzzN8cwvEvGkxkmuRIZcOCYH9+DRbpKnxh7tgsBZW+KWtaZI2j98b2/g==} + engines: {node: '>=22.0.0'} + + '@supabase/realtime-js@2.110.5': + resolution: {integrity: sha512-VtOZxw5jfrc37KgIfFdp9SOFJjtvJ0qU+gT8CPQjL7cqy4DJlaTXacw5aBBogLZksqAI0YN67qhlDaMKFwfOuw==} + engines: {node: '>=22.0.0'} + + '@supabase/storage-js@2.110.5': + resolution: {integrity: sha512-7GkOZlrYknVGVPyijoDbu5OXGvQ2oQ6dkU0juAkIMPZ9QgtmJ8IPrxe76zGSbmzbaC1GdKw7pqeXEi2HT67sbA==} + engines: {node: '>=22.0.0'} + + '@supabase/supabase-js@2.110.5': + resolution: {integrity: sha512-cAO1Nm+CCogRNVXN93bBkh0vjOdLM5e6J9gB/cHV9Lqni/gEmN2HJFrnn4NI33GYIfmlh4Wbm6siH+XXRgpexA==} + engines: {node: '>=22.0.0'} + + '@swc/core-darwin-arm64@1.15.43': + resolution: {integrity: sha512-v1aVuvXdo/BHxJzco9V2xpHrvwWmhfS8t6gziY5wJxd+Z2h8AeJRnAwPD8itCDaGXVBwJ/CaKfxEzTkG0Va0OA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + + '@swc/core-darwin-x64@1.15.43': + resolution: {integrity: sha512-lp3d4Lamc8dt5huYdGLSR+9hLxmfr1jb0l+4XXG2zPqZwYWRN9R0U2qYoTrggiU2RWW0oV9VbWM3kBnqIc2kdQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + + '@swc/core-linux-arm-gnueabihf@1.15.43': + resolution: {integrity: sha512-JWTQQELtsG5GgphDrr/XqqmM2pDN3cZqbMS0Mrg+iTiXL3F74sn/S2IyYE/5u4h2KLkTf9qQ7dXyxsbx7YzkeA==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + + '@swc/core-linux-arm64-gnu@1.15.43': + resolution: {integrity: sha512-B4otJRdPWIsmiSBf0uG7Z/+vMWmkufjz5MmYxubwKuZazDW14Zd3symga1N62QR4RT+kEFeHEgsXfZGyn/w0hw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@swc/core-linux-arm64-musl@1.15.43': + resolution: {integrity: sha512-6zB6OnpViBxYy4tgY3v2i6AZY9fwkcHZ032UOwtwUuW1d19sdT07qF0kZe6/3UR1tUaK6jjg2rmVcUIBCEYVjQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@swc/core-linux-ppc64-gnu@1.15.43': + resolution: {integrity: sha512-coxE1ZWdB3uSDVNoEtYNrRi/1epvckZx9cTJ8ICUxTMTxGk+yvQ/Twacp3ruZSaMPGCriUjP86C37VhaT6nyRg==} + engines: {node: '>=10'} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@swc/core-linux-s390x-gnu@1.15.43': + resolution: {integrity: sha512-lXfLhs+LpBsD5inuYx+YDH5WsPPBQ95KPUiy8P5wq9ob9xKDZFqwNfU2QW6bGO8NqRO/H9JQomTSt5Yyh+FGfA==} + engines: {node: '>=10'} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@swc/core-linux-x64-gnu@1.15.43': + resolution: {integrity: sha512-07XnKwTmKy8TGOZG3D9fRnLWGynxPjwQnZLVmBFbo6F+7vHYzBIOuwXEhemrChBWb6yDNZsVCcMWCPX6FDD2xg==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@swc/core-linux-x64-musl@1.15.43': + resolution: {integrity: sha512-TJc+bsSIaBh+hZvZ5GRtW/K1bw66TJ9vsUwvVIsZdiWxU5ObLwZvfcnZ3UpgVfMnFibRes9uriJrQNBHEEogRQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@swc/core-win32-arm64-msvc@1.15.43': + resolution: {integrity: sha512-jfd7s2/bUQYkOHLs+LWQNKZdmDa8+sufKLllhpWAhVQ2GDCwsHe3vR/j+OSiItZNtkzFuaawa3+SAKz9y5gYfw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + + '@swc/core-win32-ia32-msvc@1.15.43': + resolution: {integrity: sha512-rLAE8JvucqEW1ZGohxPQrQWPBQeJG4+ypKbWfdlU/qmKScvCkxf9/Jxnzki1dkUQCQ7P5Enp13RlvqOlvx/32g==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + + '@swc/core-win32-x64-msvc@1.15.43': + resolution: {integrity: sha512-h8MLDHZcfIukwQWj03rIJZx1I0E81AYj2X7J/nGErG4nz+QAv6G1Z+peotvinL3lqpbo32tLYSMFo32/ySzxKg==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + + '@swc/core@1.15.43': + resolution: {integrity: sha512-1CuKjFkPxIgGdeHVuNbkxmBxkcbdc08u0aiI43pFq6yY1tTVKmXT9hFEooyyKs/sJ3xf1GPHyEwTtk9Xl8dvQw==} + engines: {node: '>=10'} + peerDependencies: + '@swc/helpers': '>=0.5.17' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + + '@swc/helpers@0.5.23': + resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} + + '@swc/jest@0.2.39': + resolution: {integrity: sha512-eyokjOwYd0Q8RnMHri+8/FS1HIrIUKK/sRrFp8c1dThUOfNeCWbLmBP1P5VsKdvmkd25JaH+OKYwEYiAYg9YAA==} + engines: {npm: '>= 7.0.0'} + peerDependencies: + '@swc/core': '*' + + '@swc/types@0.1.27': + resolution: {integrity: sha512-K6h3iUlqeM946U4sXFYeahefR1YBbXJvko+hv8WS8/0BNJ4OHiHRywMnQUJCqkR7Y9+hqQ1TvEpiKqUhz7NEFg==} + + '@tokenizer/inflate@0.4.1': + resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==} + engines: {node: '>=18'} + + '@tokenizer/token@0.3.0': + resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + + '@ts-morph/common@0.19.0': + resolution: {integrity: sha512-Unz/WHmd4pGax91rdIKWi51wnVUW11QttMEPpBiBgIewnc9UQIX7UDLxr5vRlqeByXCwhkF6VabSsI0raWcyAQ==} + + '@tsconfig/node10@1.0.12': + resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + + '@tybys/wasm-util@0.9.0': + resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} + + '@types/accepts@1.3.7': + resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/body-parser@1.19.6': + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + + '@types/bonjour@3.5.13': + resolution: {integrity: sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==} + + '@types/connect-history-api-fallback@1.5.4': + resolution: {integrity: sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/content-disposition@0.5.9': + resolution: {integrity: sha512-8uYXI3Gw35MhiVYhG3s295oihrxRyytcRHjSjqnqZVDDy/xcGBRny7+Xj1Wgfhv5QzRtN2hB2dVRBUX9XW3UcQ==} + + '@types/cookiejar@2.1.5': + resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==} + + '@types/cookies@0.9.2': + resolution: {integrity: sha512-1AvkDdZM2dbyFybL4fxpuNCaWyv//0AwsuUk2DWeXyM1/5ZKm6W3z6mQi24RZ4l2ucY+bkSHzbDVpySqPGuV8A==} + + '@types/esquery@1.5.4': + resolution: {integrity: sha512-yYO4Q8H+KJHKW1rEeSzHxcZi90durqYgWVfnh5K6ZADVBjBv2e1NEveYX5yT2bffgN7RqzH3k9930m+i2yBoMA==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/express-serve-static-core@4.19.9': + resolution: {integrity: sha512-QP2ESEe/ImWY0HDwNAnK9PvEffUyhLTnWkk7KXzHfyeWAnlrDe1fN77bXl6ia8KT3wPlmA7t9/VPRpnf4Ex9sg==} + + '@types/express@4.17.25': + resolution: {integrity: sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==} + + '@types/http-assert@1.5.6': + resolution: {integrity: sha512-TTEwmtjgVbYAzZYWyeHPrrtWnfVkm8tQkP8P21uQifPgMRgjrow3XDEYqucuC8SKZJT7pUnhU/JymvjggxO9vw==} + + '@types/http-errors@2.0.5': + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + + '@types/http-proxy@1.17.17': + resolution: {integrity: sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==} + + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + + '@types/istanbul-lib-report@3.0.3': + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + + '@types/istanbul-reports@3.0.4': + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + + '@types/jest@30.0.0': + resolution: {integrity: sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/keygrip@1.0.6': + resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==} + + '@types/koa-compose@3.2.9': + resolution: {integrity: sha512-BroAZ9FTvPiCy0Pi8tjD1OfJ7bgU1gQf0eR6e1Vm+JJATy9eKOG3hQMFtMciMawiSOVnLMdmUOC46s7HBhSTsA==} + + '@types/koa@3.0.3': + resolution: {integrity: sha512-TdtNEJ7sYSrFQcVuS2ySsVqnq5EyE3oJbnfFJvkC9UtGP4Kpem5KE7r+ivHIbIAQAofSqnlB5D3vkfYO69TQpg==} + + '@types/luxon@3.7.2': + resolution: {integrity: sha512-gW+Oib+vUtGJBtNC8V9Reww0oIpusw+4m81uncg9REGZAJfqOQHfo/nkabnc7w0QReXyPqjrbWMJk6NuAkiX3Q==} + + '@types/methods@1.1.4': + resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} + + '@types/mime@1.3.5': + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + + '@types/node@22.20.1': + resolution: {integrity: sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==} + + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + + '@types/passport-http-bearer@1.0.42': + resolution: {integrity: sha512-cGezyf9hy3Cth+zWS779FR9XYhIX/DExsVZURqcSeUU/nhj0Aw8PUhvCyfS35ScwOSd5AFiFhtfWmqHa/2aYZg==} + + '@types/passport@1.0.17': + resolution: {integrity: sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==} + + '@types/qs@6.15.1': + resolution: {integrity: sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==} + + '@types/ramda@0.31.1': + resolution: {integrity: sha512-Vt6sFXnuRpzaEj+yeutA0q3bcAsK7wdPuASIzR9LXqL4gJPyFw8im9qchlbp4ltuf3kDEIRmPJTD/Fkg60dn7g==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + + '@types/retry@0.12.2': + resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==} + + '@types/send@0.17.6': + resolution: {integrity: sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==} + + '@types/send@1.2.1': + resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} + + '@types/serve-index@1.9.4': + resolution: {integrity: sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==} + + '@types/serve-static@1.15.10': + resolution: {integrity: sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==} + + '@types/sockjs@0.3.36': + resolution: {integrity: sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==} + + '@types/stack-utils@2.0.3': + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + + '@types/superagent@8.1.11': + resolution: {integrity: sha512-KA7srSW/HENDtOw9DOqaFLgWuMqN9WgjEw62lh9dpvRaZDkhdOkazASd7X7i2eMUYLHa1U37ZttnePsH5zTDHw==} + + '@types/supertest@7.2.1': + resolution: {integrity: sha512-4CbBvoYVLHL7+yhbYrZET0vsvuyXTC05aRe7dNQkwMzm56auceoy6Yu3K50uZmwfHna1os3CMSgM/3QVkUtPTw==} + + '@types/validator@13.15.10': + resolution: {integrity: sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + + '@types/yargs@17.0.35': + resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} + + '@typescript-eslint/eslint-plugin@8.63.0': + resolution: {integrity: sha512-rvwSgqT+DHpWdzfSzPatRLm02a0GlESt++9iy3hLCDY4BgkaLcl8LBi9Yh7XGFBpwcBE/K3024QuXWTpbz4FfQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.63.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.63.0': + resolution: {integrity: sha512-gwh4gvvlaVDKKxyfxMG+Gnu1u9X0OQBwyGLkbwB65dIzBKnxeRiJlNFqlI3zwVhNXJIs6qV7mlFCn/BIajlVig==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.63.0': + resolution: {integrity: sha512-e5dh0/UI0ok53AlZ5wRkXCB32z/f2jUZqPR/ygAw5WYaSw8j9EoJWlS7wQjr/dmOaqWjnPIn2m+HhVPCMWGZVQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.63.0': + resolution: {integrity: sha512-uUyfMWCnDSN8bCpcrY8nGP2BLkQ9Xn0GsipcONcpIDWhwhO4ZSyHvyS14U3X75mzxWxL3I2UZIrenTzdzcJO8A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.63.0': + resolution: {integrity: sha512-sUAbkulqBAsncKnbRP3+7CtQFRKicexnj7ZwNC6ddCR7EmrXvjvdCYMJbUIqMd6lwoEriZjwLo08aS5tSjVMHg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.63.0': + resolution: {integrity: sha512-Nzzh/OGxVCOjObjaj1CQF2RUasyYy2Jfuh+zZ3PjLzG2fYRriAiZLib9UKtO+CpQAS3YHiAS+ckZDclwqI1TPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.63.0': + resolution: {integrity: sha512-xyLtl9DUBBFrcJS4x2pIqGLH68/tC2uOa4Z7pUteW09D3bXnnXUom4dyPikzWgB7llmIc1zoeI3aoUdC4rPK/Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.63.0': + resolution: {integrity: sha512-ygBkU+B7ex5UI/gKhaqexWev79uISfIv7XQCRNYO/jmD8rGLPyWLAb3KMRT6nd8Gt9bmUBi9+iX6tBdYfOY81Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.63.0': + resolution: {integrity: sha512-fUKaeAvrTuQg/Tgt3nliAUSZHJM6DlCcfyEmxCvlX8kieWSStBX+5O5Fnidtc3i2JrH+9c/GL4RY2iasd/GPTA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.63.0': + resolution: {integrity: sha512-UexrHGnGTpbuQHct2ExOc2ZcFbGUS9FOesCxxqdBGcpI1BxYu/LZ6U8Aq6/72XtF/qRBk9nhuGHFJIXXMhPMdw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@ungap/structured-clone@1.3.3': + resolution: {integrity: sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==} + + '@unrs/resolver-binding-android-arm-eabi@1.12.2': + resolution: {integrity: sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==} + cpu: [arm] + os: [android] + + '@unrs/resolver-binding-android-arm64@1.12.2': + resolution: {integrity: sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==} + cpu: [arm64] + os: [android] + + '@unrs/resolver-binding-darwin-arm64@1.12.2': + resolution: {integrity: sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==} + cpu: [arm64] + os: [darwin] + + '@unrs/resolver-binding-darwin-x64@1.12.2': + resolution: {integrity: sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==} + cpu: [x64] + os: [darwin] + + '@unrs/resolver-binding-freebsd-x64@1.12.2': + resolution: {integrity: sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==} + cpu: [x64] + os: [freebsd] + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2': + resolution: {integrity: sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2': + resolution: {integrity: sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-gnu@1.12.2': + resolution: {integrity: sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-arm64-musl@1.12.2': + resolution: {integrity: sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-linux-loong64-gnu@1.12.2': + resolution: {integrity: sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-loong64-musl@1.12.2': + resolution: {integrity: sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2': + resolution: {integrity: sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2': + resolution: {integrity: sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-riscv64-musl@1.12.2': + resolution: {integrity: sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-linux-s390x-gnu@1.12.2': + resolution: {integrity: sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-x64-gnu@1.12.2': + resolution: {integrity: sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-x64-musl@1.12.2': + resolution: {integrity: sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-openharmony-arm64@1.12.2': + resolution: {integrity: sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==} + cpu: [arm64] + os: [openharmony] + + '@unrs/resolver-binding-wasm32-wasi@1.12.2': + resolution: {integrity: sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': + resolution: {integrity: sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.12.2': + resolution: {integrity: sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.12.2': + resolution: {integrity: sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==} + cpu: [x64] + os: [win32] + + '@webassemblyjs/ast@1.14.1': + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} + + '@webassemblyjs/floating-point-hex-parser@1.13.2': + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} + + '@webassemblyjs/helper-api-error@1.13.2': + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} + + '@webassemblyjs/helper-buffer@1.14.1': + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} + + '@webassemblyjs/helper-numbers@1.13.2': + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} + + '@webassemblyjs/helper-wasm-section@1.14.1': + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} + + '@webassemblyjs/ieee754@1.13.2': + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} + + '@webassemblyjs/leb128@1.13.2': + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} + + '@webassemblyjs/utf8@1.13.2': + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} + + '@webassemblyjs/wasm-edit@1.14.1': + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} + + '@webassemblyjs/wasm-gen@1.14.1': + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} + + '@webassemblyjs/wasm-opt@1.14.1': + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} + + '@webassemblyjs/wasm-parser@1.14.1': + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} + + '@webassemblyjs/wast-printer@1.14.1': + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + + '@xtuc/ieee754@1.2.0': + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + + '@xtuc/long@4.2.2': + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + + '@yarnpkg/lockfile@1.1.0': + resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==} + + '@zkochan/js-yaml@0.0.7': + resolution: {integrity: sha512-nrUSn7hzt7J6JWgWGz78ZYI8wj+gdIJdk0Ynjpp8l+trkn58Uqsf6RYrYkEK+3X18EX+TNdtJI0WxAtc+L84SQ==} + hasBin: true + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + acorn-import-phases@1.0.4: + resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} + engines: {node: '>=10.13.0'} + peerDependencies: + acorn: ^8.14.0 + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-walk@8.3.5: + resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} + engines: {node: '>=0.4.0'} + + acorn@8.17.0: + resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} + engines: {node: '>=0.4.0'} + hasBin: true + + address@2.0.3: + resolution: {integrity: sha512-XNAb/a6TCqou+TufU8/u11HCu9x1gYvOoxLwtlXgIqmkrYQADVv6ljyW2zwiPhHz9R1gItAWpuDrdJMmrOBFEA==} + engines: {node: '>= 16.0.0'} + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-keywords@3.5.2: + resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} + peerDependencies: + ajv: ^6.9.1 + + ajv-keywords@5.1.0: + resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + peerDependencies: + ajv: ^8.8.2 + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + ajv@8.18.0: + resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-escapes@7.3.0: + resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} + engines: {node: '>=18'} + + ansi-html-community@0.0.8: + resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==} + engines: {'0': node >= 0.8.0} + hasBin: true + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + append-field@1.0.0: + resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} + + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + + array-timsort@1.0.3: + resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} + + asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + + asn1js@3.0.10: + resolution: {integrity: sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==} + engines: {node: '>=12.0.0'} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + autoprefixer@10.5.2: + resolution: {integrity: sha512-rD5t5DwOjJdmSORcTq64j8MawTC+tbQ+HHqjR4NDumamy/ambn1UJrlKL+KdwujWxMkFjPM3pPHOEA9tl4767Q==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + axios@1.16.1: + resolution: {integrity: sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==} + + axios@1.18.1: + resolution: {integrity: sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==} + + babel-jest@30.3.0: + resolution: {integrity: sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + '@babel/core': ^7.11.0 || ^8.0.0-0 + + babel-jest@30.4.1: + resolution: {integrity: sha512-fATAbM8piYxkiXQp3RBXmZHxZVNJZAVXXfyeyCN2Tida3+qJ8ea9UxhiJ2y4fLO90ZImKt6k9FlcH2+rLkJGhw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + '@babel/core': ^7.11.0 || ^8.0.0-0 + + babel-loader@9.2.1: + resolution: {integrity: sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==} + engines: {node: '>= 14.15.0'} + peerDependencies: + '@babel/core': ^7.12.0 + webpack: '>=5' + + babel-plugin-const-enum@1.2.0: + resolution: {integrity: sha512-o1m/6iyyFnp9MRsK1dHF3bneqyf3AlM2q3A/YbgQr2pCat6B6XJVDv2TXqzfY2RYUi4mak6WAksSBPlyYGx9dg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + babel-plugin-istanbul@7.0.1: + resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==} + engines: {node: '>=12'} + + babel-plugin-jest-hoist@30.3.0: + resolution: {integrity: sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + babel-plugin-jest-hoist@30.4.0: + resolution: {integrity: sha512-9EdtWM/sSfXLOGLwSn+GS6pIXyBnL07/8gyJlwFXjWy4DxMOyItqyUT29d4lQiS380EZwYlX7/At4PgBS+m2aA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + + babel-plugin-polyfill-corejs2@0.4.17: + resolution: {integrity: sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.13.0: + resolution: {integrity: sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.14.2: + resolution: {integrity: sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.8: + resolution: {integrity: sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-transform-typescript-metadata@0.3.2: + resolution: {integrity: sha512-mWEvCQTgXQf48yDqgN7CH50waTyYBeP2Lpqx4nNWab9sxEpdXVeKgfj1qYI2/TgUPQtNFZ85i3PemRtnXVYYJg==} + peerDependencies: + '@babel/core': ^7 + '@babel/traverse': ^7 + peerDependenciesMeta: + '@babel/traverse': + optional: true + + babel-preset-current-node-syntax@1.2.0: + resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} + peerDependencies: + '@babel/core': ^7.0.0 || ^8.0.0-0 + + babel-preset-jest@30.3.0: + resolution: {integrity: sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + '@babel/core': ^7.11.0 || ^8.0.0-beta.1 + + babel-preset-jest@30.4.0: + resolution: {integrity: sha512-lBY4jxsNmCnSiu7kquw8ZC9F4+XLMOKypT3RnNHPvU2Kpd4W0xaPuLr5ZkRyOsvLYAY4yaW1ZwTW4xB7NIiZzg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + '@babel/core': ^7.11.0 || ^8.0.0-beta.1 + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.3: + resolution: {integrity: sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g==} + engines: {node: 20 || >=22} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + baseline-browser-mapping@2.10.43: + resolution: {integrity: sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + basic-auth@2.0.1: + resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} + engines: {node: '>= 0.8'} + + batch@0.6.1: + resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} + + better-supabase-types@2.7.6: + resolution: {integrity: sha512-BHzbSGb8bLiozsTRZG7ds0cbZTxEFdLcbziMOSPcUuFrkFG7HAPvTV/s5heWmjC0VIKKw6HMMmMS4bZ4w/GOTw==} + hasBin: true + + big.js@5.2.2: + resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + body-parser@1.20.6: + resolution: {integrity: sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + body-parser@2.3.0: + resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} + engines: {node: '>=18'} + + bonjour-service@1.4.3: + resolution: {integrity: sha512-2Kd5UYlFUVgAKMTyuBLl6w49wqfOnbxHqmuH0oCl/n7TfAikR0zoowNOP5BU4dfXmm+Vr9JyEN370auSMx+CNg==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + brace-expansion@1.1.16: + resolution: {integrity: sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==} + + brace-expansion@2.1.2: + resolution: {integrity: sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==} + + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + engines: {node: 18 || 20 || >=22} + + brace-expansion@5.0.7: + resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==} + engines: {node: 18 || 20 || >=22} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.28.6: + resolution: {integrity: sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bs-logger@0.2.6: + resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} + engines: {node: '>= 6'} + + bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + bundle-name@4.1.0: + resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} + engines: {node: '>=18'} + + busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + bytestreamjs@2.0.1: + resolution: {integrity: sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==} + engines: {node: '>=6.0.0'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + caniuse-api@3.0.0: + resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} + + caniuse-lite@1.0.30001805: + resolution: {integrity: sha512-52noaS3DubycKSXaU30TwPGIp+POyQSUVa5jBEq3vkRkY0kjyb3LQgvhU6WGyCcyXqVLWO0Cw0Q6BSdD0kUfVA==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} + engines: {node: '>=6.0'} + + ci-info@4.4.0: + resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} + engines: {node: '>=8'} + + cjs-module-lexer@2.2.0: + resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} + + class-transformer@0.5.1: + resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==} + + class-validator@0.15.1: + resolution: {integrity: sha512-LqoS80HBBSCVhz/3KloUly0ovokxpdOLR++Al3J3+dHXWt9sTKlKd4eYtoxhxyUjoe5+UcIM+5k9MIxyBWnRTw==} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-spinners@2.6.1: + resolution: {integrity: sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==} + engines: {node: '>=6'} + + cli-truncate@5.2.0: + resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} + engines: {node: '>=20'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clone-deep@4.0.1: + resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} + engines: {node: '>=6'} + + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + + co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + + code-block-writer@12.0.0: + resolution: {integrity: sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==} + + collect-v8-coverage@1.0.3: + resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + colorjs.io@0.5.2: + resolution: {integrity: sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==} + + columnify@1.6.0: + resolution: {integrity: sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==} + engines: {node: '>=8.0.0'} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + engines: {node: '>=20'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + comment-json@5.0.0: + resolution: {integrity: sha512-uiqLcOiVDJtBP8WGkZHEP+FZIhTzP1dxvn59EfoYUi9gqupjrBWVQkO2atDrbnKPwLeotFYDsuNb26uBMqB+hw==} + engines: {node: '>= 6'} + + common-path-prefix@3.0.0: + resolution: {integrity: sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==} + + component-emitter@1.3.1: + resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} + + compressible@2.0.18: + resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} + engines: {node: '>= 0.6'} + + compression@1.8.1: + resolution: {integrity: sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==} + engines: {node: '>= 0.8.0'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + concat-stream@2.0.0: + resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} + engines: {'0': node >= 6.0} + + confusing-browser-globals@1.0.11: + resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} + + connect-history-api-fallback@2.0.0: + resolution: {integrity: sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==} + engines: {node: '>=0.8'} + + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-disposition@1.1.0: + resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} + engines: {node: '>=18'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + content-type@2.0.0: + resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} + engines: {node: '>=18'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-signature@1.0.7: + resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cookiejar@2.1.4: + resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} + + copy-anything@2.0.6: + resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} + + copy-webpack-plugin@14.0.0: + resolution: {integrity: sha512-3JLW90aBGeaTLpM7mYQKpnVdgsUZRExY55giiZgLuX/xTQRUs1dOCwbBnWnvY6Q6rfZoXMNwzOQJCSZPppfqXA==} + engines: {node: '>= 20.9.0'} + peerDependencies: + webpack: ^5.1.0 + + core-js-compat@3.49.0: + resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cors@2.8.6: + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} + engines: {node: '>= 0.10'} + + corser@2.0.1: + resolution: {integrity: sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==} + engines: {node: '>= 0.4.0'} + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + + cosmiconfig@8.3.6: + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cosmiconfig@9.0.2: + resolution: {integrity: sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + cron@4.4.0: + resolution: {integrity: sha512-fkdfq+b+AHI4cKdhZlppHveI/mgz2qpiYxcm+t5E5TsxX7QrLS1VE0+7GENEk9z0EeGPcpSciGv6ez24duWhwQ==} + engines: {node: '>=18.x'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-declaration-sorter@7.4.0: + resolution: {integrity: sha512-LTuzjPoyA2vMGKKcaOqKSp7Ub2eGrNfKiZH4LpezxpNrsICGCSFvsQOI29psISxNZtaXibkC2CXzrQ5enMeGGw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.0.9 + + css-loader@6.11.0: + resolution: {integrity: sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==} + engines: {node: '>= 12.13.0'} + peerDependencies: + '@rspack/core': 0.x || 1.x + webpack: ^5.0.0 + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true + + css-minimizer-webpack-plugin@8.0.0: + resolution: {integrity: sha512-9bEpzHs8gEq6/cbEj418jXL/YWjBUD2YTLLk905Npt2JODqnRITin0+So5Vx4Dp5vyi2Lpt9pp2QHzQ7fdxNrw==} + engines: {node: '>= 20.9.0'} + peerDependencies: + '@parcel/css': '*' + '@swc/css': '*' + clean-css: '*' + csso: '*' + esbuild: '*' + lightningcss: '*' + webpack: ^5.0.0 + peerDependenciesMeta: + '@parcel/css': + optional: true + '@swc/css': + optional: true + clean-css: + optional: true + csso: + optional: true + esbuild: + optional: true + lightningcss: + optional: true + + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + + css-tree@2.2.1: + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + css-tree@3.2.1: + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + cssnano-preset-default@7.0.17: + resolution: {integrity: sha512-11qO63A+czwguQFJCaTdICvbaxn0pJzz/XghLlv+OT7WyToDxAMR0Xb3/26/l0y0hQJywwNbj/SLSQlGBHE1OA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + cssnano-utils@5.0.3: + resolution: {integrity: sha512-ynIREMICLxkxm7e9bCR9sh75s4Q5drICi0ua1yxo5jH2XPBqSKkl4dOh4EbFqtUmnTMhRffHgYL0EKKkMjtJTg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + cssnano@7.1.9: + resolution: {integrity: sha512-uPR75+5Dk/WJ/YSPR1/YDHdwMM9c5FsaARljfKWgeCKLKOtJ0we21xy/RcCjn53fZnD/f6yYEIZ8pu18+GnbNQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + csso@5.0.5: + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.1: + resolution: {integrity: sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + dedent@1.7.2: + resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + default-browser-id@5.0.1: + resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} + engines: {node: '>=18'} + + default-browser@5.5.0: + resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} + engines: {node: '>=18'} + + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + + define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + depd@1.1.2: + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + engines: {node: '>= 0.6'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + + detect-node@2.1.0: + resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + + detect-port@2.1.0: + resolution: {integrity: sha512-epZuWb/6Q62L+nDHJc/hQAqf8pylsqgk3BpZXVBx1CDnr3nkrVNn73Uu1rXcFzkNcc+hkP3whuOg7JZYaQB65Q==} + engines: {node: '>= 16.0.0'} + hasBin: true + + dezalgo@1.0.4: + resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} + + diff@4.0.4: + resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} + engines: {node: '>=0.3.1'} + + dns-packet@5.6.1: + resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} + engines: {node: '>=6'} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dotenv-expand@12.0.3: + resolution: {integrity: sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==} + engines: {node: '>=12'} + + dotenv@16.4.7: + resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + eciesjs@0.5.0: + resolution: {integrity: sha512-s0J9SEVYAEPg7J63GFMApLYzPH9VNIQIyC6s15JpnqVc0TqcKWdbgFlnAweEBRyMmko2dcs2sfC83Hj4J43tuA==} + engines: {bun: '>=1', deno: '>=2.7.10', node: '>=16'} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + ejs@5.0.1: + resolution: {integrity: sha512-COqBPFMxuPTPspXl2DkVYaDS3HtrD1GpzOGkNTJ1IYkifq/r9h8SVEFrjA3D9/VJGOEoMQcrlhpntcSUrM8k6A==} + engines: {node: '>=0.12.18'} + hasBin: true + + electron-to-chromium@1.5.389: + resolution: {integrity: sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==} + + emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + emojis-list@3.0.0: + resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} + engines: {node: '>= 4'} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + enhanced-resolve@5.24.2: + resolution: {integrity: sha512-rpsZEGT1jFuve6QlpyRp9ckQ+kN61hvF9BzCPyMdaKTm8UJce96KBn3sorXOFXlzjPrs3Vc4T1NsSroZ3PxlFw==} + engines: {node: '>=10.13.0'} + + enquirer@2.3.6: + resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==} + engines: {node: '>=8.6'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + envinfo@7.21.0: + resolution: {integrity: sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==} + engines: {node: '>=4'} + hasBin: true + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + errno@0.1.8: + resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} + hasBin: true + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@2.3.1: + resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-prettier@10.1.8: + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@9.39.5: + resolution: {integrity: sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + exit-x@0.2.2: + resolution: {integrity: sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==} + engines: {node: '>= 0.8.0'} + + expect@30.3.0: + resolution: {integrity: sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + expect@30.4.1: + resolution: {integrity: sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + express@4.22.2: + resolution: {integrity: sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==} + engines: {node: '>= 0.10.0'} + + express@5.2.1: + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + engines: {node: '>= 18'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + fast-uri@3.1.3: + resolution: {integrity: sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + faye-websocket@0.11.4: + resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} + engines: {node: '>=0.8.0'} + + fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + figures@3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + file-type@21.3.4: + resolution: {integrity: sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==} + engines: {node: '>=20'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@1.3.2: + resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} + engines: {node: '>= 0.8'} + + finalhandler@2.1.1: + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} + + find-cache-dir@4.0.0: + resolution: {integrity: sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==} + engines: {node: '>=14.16'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + find-up@6.3.0: + resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flat@5.0.2: + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + hasBin: true + + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + fork-ts-checker-webpack-plugin@9.1.0: + resolution: {integrity: sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==} + engines: {node: '>=14.21.3'} + peerDependencies: + typescript: '>3.6.0' + webpack: ^5.11.0 + + form-data@4.0.6: + resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} + engines: {node: '>= 6'} + + formidable@3.5.4: + resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==} + engines: {node: '>=14.0.0'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs-monkey@1.1.0: + resolution: {integrity: sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.6.0: + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} + engines: {node: '>=18'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + get-them-args@1.3.2: + resolution: {integrity: sha512-LRn8Jlk+DwZE4GTlDbT3Hikd1wSHgLMme/+7ddlqKd7ldwR6LjJgTVWzBnR01wnYGe4KgrXjg287RaI22UHmAw==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob-to-regex.js@1.2.0: + resolution: {integrity: sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + glob@10.5.0: + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@17.7.0: + resolution: {integrity: sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==} + engines: {node: '>=18'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + handle-thing@2.0.1: + resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==} + + handlebars@4.7.9: + resolution: {integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==} + engines: {node: '>=0.4.7'} + hasBin: true + + harmony-reflect@1.6.2: + resolution: {integrity: sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + hpack.js@2.1.6: + resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==} + + html-encoding-sniffer@3.0.0: + resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} + engines: {node: '>=12'} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + http-deceiver@1.2.7: + resolution: {integrity: sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==} + + http-errors@1.8.1: + resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==} + engines: {node: '>= 0.6'} + + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + + http-parser-js@0.5.10: + resolution: {integrity: sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==} + + http-proxy-middleware@2.0.10: + resolution: {integrity: sha512-RKzRWNPxUZqbuk3BC5mGVJbBnWgr+diEnjJexIOytFbBzDy88Fbh/YvBr3DsNrl1jYAfjWfpATEv0NO35FDuPQ==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@types/express': ^4.17.13 + peerDependenciesMeta: + '@types/express': + optional: true + + http-proxy@1.18.1: + resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} + engines: {node: '>=8.0.0'} + + http-server@14.1.1: + resolution: {integrity: sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==} + engines: {node: '>=12'} + hasBin: true + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + + hyperdyperid@1.2.0: + resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==} + engines: {node: '>=10.18'} + + iceberg-js@0.8.1: + resolution: {integrity: sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==} + engines: {node: '>=20.0.0'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.7.3: + resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} + engines: {node: '>=0.10.0'} + + icss-utils@5.1.0: + resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + identity-obj-proxy@3.0.0: + resolution: {integrity: sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==} + engines: {node: '>=4'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + ignore@7.0.6: + resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==} + engines: {node: '>= 4'} + + image-size@0.5.5: + resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} + engines: {node: '>=0.10.0'} + hasBin: true + + immutable@5.1.9: + resolution: {integrity: sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + import-local@3.2.0: + resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} + engines: {node: '>=8'} + hasBin: true + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + interpret@3.1.1: + resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==} + engines: {node: '>=10.13.0'} + + ip-regex@2.1.0: + resolution: {integrity: sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==} + engines: {node: '>=4'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + ipaddr.js@2.4.0: + resolution: {integrity: sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ==} + engines: {node: '>= 10'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} + engines: {node: '>= 0.4'} + + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + engines: {node: '>=18'} + + is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + + is-network-error@1.3.2: + resolution: {integrity: sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA==} + engines: {node: '>=16'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-obj@3.0.0: + resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==} + engines: {node: '>=10'} + + is-plain-object@2.0.4: + resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} + engines: {node: '>=0.10.0'} + + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + is-url@1.2.4: + resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==} + + is-what@3.14.1: + resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} + + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + + is-wsl@3.1.1: + resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} + engines: {node: '>=16'} + + is2@2.0.1: + resolution: {integrity: sha512-+WaJvnaA7aJySz2q/8sLjMb2Mw14KTplHmSwcSpZ/fWJPkUmqw3YTzSWbPJ7OAwRvdYTWF2Wg+yYJ1AdP5Z8CA==} + engines: {node: '>=v0.10.0'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isobject@3.0.1: + resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} + engines: {node: '>=0.10.0'} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + engines: {node: '>=10'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@5.0.6: + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + + iterare@1.2.1: + resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} + engines: {node: '>=6'} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jest-changed-files@30.3.0: + resolution: {integrity: sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-circus@30.3.0: + resolution: {integrity: sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-circus@30.4.2: + resolution: {integrity: sha512-rvHH7VlY6LgbJXJTQ87GW62g1FntOtbhh0zT+v04kC+pgL6aBKyYINXxWukCpj3dcIBMw5/XUbtDS9dU9JTXeQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-cli@30.3.0: + resolution: {integrity: sha512-l6Tqx+j1fDXJEW5bqYykDQQ7mQg+9mhWXtnj+tQZrTWYHyHoi6Be8HPumDSA+UiX2/2buEgjA58iJzdj146uCw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jest-config@30.3.0: + resolution: {integrity: sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + '@types/node': '*' + esbuild-register: '>=3.4.0' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + esbuild-register: + optional: true + ts-node: + optional: true + + jest-config@30.4.2: + resolution: {integrity: sha512-rNHAShJQqQwFNoL0hbf3BphSBOWnpOUAKvidLS/AjNVLPfoj5mSf4jQMfW3cYOs6hXeZC7nF7mDHaBnbxELOzg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + '@types/node': '*' + esbuild-register: '>=3.4.0' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + esbuild-register: + optional: true + ts-node: + optional: true + + jest-diff@30.3.0: + resolution: {integrity: sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-diff@30.4.1: + resolution: {integrity: sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-docblock@30.2.0: + resolution: {integrity: sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-docblock@30.4.0: + resolution: {integrity: sha512-ZPMabUZCx5MpbZ2eBYSvZ0J8fvo3dR9oM+eeUpb3aKNQFuS2tu3Duw1TNlMoP8k3WQgKGJuhcMFvwcVuq6T7oA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-each@30.3.0: + resolution: {integrity: sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-each@30.4.1: + resolution: {integrity: sha512-/8MJbH6fuj48TstjrMf+u/pd06Qezz5xOXvZA6442heNOWr8bdeoGZX2d9fCn028CoMgYmroH9//zky5GfyYmA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-environment-node@30.3.0: + resolution: {integrity: sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-environment-node@30.4.1: + resolution: {integrity: sha512-4FZYVOk85hz2AyT6BbarKy9u37g6DbrDyCdFhsnDdXqyrueYQvB+0zO4f/kqLCRD0BsPRXPMNJeQwihKZV8naw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-haste-map@30.3.0: + resolution: {integrity: sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-haste-map@30.4.1: + resolution: {integrity: sha512-rFrcONd8jeFsyw+Z9CrScJgglRf2+NFmNam8dKu7n+SoHqNYT47mn0DdEcVUZJpvh7Iz6/si7f7yUH7GJHVgnw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-leak-detector@30.3.0: + resolution: {integrity: sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-leak-detector@30.4.1: + resolution: {integrity: sha512-IpmyiioeHxiWDhesHnUFmOxcTzwCwKpgACgWajtAP+nYQXiY7DakTxB6Bx9JFiRMljr0AX1PvnQdaU1KFoz6NQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-matcher-utils@30.3.0: + resolution: {integrity: sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-matcher-utils@30.4.1: + resolution: {integrity: sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-message-util@30.3.0: + resolution: {integrity: sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-message-util@30.4.1: + resolution: {integrity: sha512-kwCKIvq0MCW1HzLoGola9Te6JUdzgV0loyKJ3Qghrkz9i5/RRIHsL95BMQc2HBBhlBKC4j22K9p11TGHH8RBpQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-mock@30.3.0: + resolution: {integrity: sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-mock@30.4.1: + resolution: {integrity: sha512-/i8SVb8/NSB7RfNi8gfqu8gxLV23KaL5EpAttyb9iz8qWRIqXRLflycz/32wXsYkOnaUlx8NAKnJYtpsmXUmfw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-pnp-resolver@1.2.3: + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + + jest-regex-util@30.0.1: + resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-regex-util@30.4.0: + resolution: {integrity: sha512-mWlvLviKIgIQ8VCuM1xRdD0TWp3zlzionlmDBjuXVBs+VkmXq6FgW9T4Emr7oGz/Rk6feDCGyiugolcQEyp3mg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-resolve-dependencies@30.3.0: + resolution: {integrity: sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-resolve@30.3.0: + resolution: {integrity: sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-resolve@30.4.1: + resolution: {integrity: sha512-Zry8Yq/yJcNAZ7dJ5F2heic8AheXvbFZ7XI5V+h28nrYZ7Qoyy4dItq8OodjnYD270mvX+ZudmrNV9cysqhW5Q==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-runner@30.3.0: + resolution: {integrity: sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-runner@30.4.2: + resolution: {integrity: sha512-2dw0PslVYXxffXGpLo+Ejad+KcI1Qkjn7f4X4619gf21oCUmL+SPfjqIa/losUem3yEOvfNZe/F1HWUcNpODcg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-runtime@30.3.0: + resolution: {integrity: sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-runtime@30.4.2: + resolution: {integrity: sha512-3/5e8iPz2k/VLqlr8DgTftYyLUv8Su3FkCAO2/Od81UsUTpSxOrS6O5x5KkoQwyUjmpYyDJKeyAvg2T2nvpNkQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-snapshot@30.3.0: + resolution: {integrity: sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-snapshot@30.4.1: + resolution: {integrity: sha512-tEOkkfOMppUyeiHwjZswOQ3lcnoTnws/q5FnGIaeIh/jmoU0ZlgMYRR8sTlTj+nNGCoJ0RDq6SfxGxCsyMTPmw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-util@30.3.0: + resolution: {integrity: sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-util@30.4.1: + resolution: {integrity: sha512-vjQb1sACEiv13DKJMDToJpzVW0joCsIQrmbg0fi7CyOOt+g9jTuQl2A216pWRBYhOVt53XbL/2LbMKg1BECWOw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-validate@30.3.0: + resolution: {integrity: sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-validate@30.4.1: + resolution: {integrity: sha512-PDWi4SOwLnwqNDfHZjOcsEFyZ4fc/2W2gVL3DEoyqnB6jCQMLRtfBong8s6omIw3lI0HWOus12xfnFmQtjW3fw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-watcher@30.3.0: + resolution: {integrity: sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-watcher@30.4.1: + resolution: {integrity: sha512-/l9UonmvCwjHH7d2h3iAwIloLc1H0S8mJZ/LNK3i86hqwPAz8otUJjP9MfYtz9Tt77Su5FD2xGjZn8d31IZHlw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + + jest-worker@30.3.0: + resolution: {integrity: sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-worker@30.4.1: + resolution: {integrity: sha512-SHynN/q/QD++iNyvMdy+WMmbCGk8jIsNcRxycXbWubSOhvo6T+j2afcfUSl+3hYsiBebOTo0cT7c2H7CXugu1g==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest@30.3.0: + resolution: {integrity: sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + jose@6.2.3: + resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@3.15.0: + resolution: {integrity: sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==} + hasBin: true + + js-yaml@4.3.0: + resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonc-eslint-parser@2.4.2: + resolution: {integrity: sha512-1e4qoRgnn448pRuMvKGsFFymUCquZV0mpGgOyIKNgD3JVDTsVJyRBGH/Fm0tBb8WsWGgmB1mDe6/yJMQM37DUA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + + jsonfile@6.2.1: + resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kill-port@1.6.1: + resolution: {integrity: sha512-un0Y55cOM7JKGaLnGja28T38tDDop0AQ8N0KlAdyh+B1nmMoX8AnNmqPNZbS3mUMgiST51DCVqmbFT1gNJpVNw==} + hasBin: true + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + launch-editor@2.14.1: + resolution: {integrity: sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==} + + less-loader@12.3.3: + resolution: {integrity: sha512-F0+ErFFDj3Pt+nVrCN6VlEGEzocv9s7x/aR9v2riI+WM83UAfTYBDBjGPJnT55lBLR6JSI4fmWblt+aA2JN6/w==} + engines: {node: '>= 18.12.0'} + peerDependencies: + '@rspack/core': 0.x || ^1.0.0 || ^2.0.0-0 + less: ^3.5.0 || ^4.0.0 + webpack: ^5.0.0 + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true + + less@4.5.1: + resolution: {integrity: sha512-UKgI3/KON4u6ngSsnDADsUERqhZknsVZbnuzlRZXLQCmfC/MDld42fTydUE9B+Mla1AL6SJ/Pp6SlEFi/AVGfw==} + engines: {node: '>=14'} + hasBin: true + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + libphonenumber-js@1.13.8: + resolution: {integrity: sha512-80xal1m93rADejw2pMp2MSzFhHCPLEspjHxnH2UtqI+DgAmElsbmLMiqk9niwH9NWAfjsRtaJI+qBrOEmRx9nQ==} + + license-webpack-plugin@4.0.2: + resolution: {integrity: sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==} + peerDependencies: + webpack: '*' + peerDependenciesMeta: + webpack: + optional: true + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lines-and-columns@2.0.3: + resolution: {integrity: sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lint-staged@17.0.8: + resolution: {integrity: sha512-B2P/d+jVW0UXOQ0MVMLrB/9ydA1P+zz6jYfdrbbEd9ur3S2rcbduFWKiUCC02Sm5hbC8nrm7y24WuYMG54HfxA==} + engines: {node: '>=22.22.1'} + hasBin: true + + listr2@10.2.2: + resolution: {integrity: sha512-JtNtbZj8q5BnDMR7trpwvwk3RIrANtIVzEUm8w7amp6xelLgyuq+4WZoTH913XaQAoH/cNdYhaNzBPA2U3xbDw==} + engines: {node: '>=22.13.0'} + + load-esm@1.0.3: + resolution: {integrity: sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==} + engines: {node: '>=13.2.0'} + + loader-runner@4.3.2: + resolution: {integrity: sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==} + engines: {node: '>=6.11.5'} + + loader-utils@2.0.4: + resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} + engines: {node: '>=8.9.0'} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + luxon@3.7.2: + resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} + engines: {node: '>=12'} + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + make-dir@2.1.0: + resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} + engines: {node: '>=6'} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + makeerror@1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mdn-data@2.0.28: + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} + + mdn-data@2.27.1: + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + + memfs@3.5.3: + resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} + engines: {node: '>= 4.0.0'} + + memfs@4.64.0: + resolution: {integrity: sha512-Kw72fgY7Wn+sD8KmtNWSafl1dz0UvAsE/PHs3YVfLiaZuA3HxNm9sRLqAu0ATiBGJvME1PxZXbBZPv5GycDeAw==} + peerDependencies: + tslib: '2' + + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mime@2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + mini-css-extract-plugin@2.4.7: + resolution: {integrity: sha512-euWmddf0sk9Nv1O0gfeeUAvAkoSlWncNLF77C0TP2+WoPvy8mAHKOzMajcCz2dzvyt3CNgxb1obIEVFIRxaipg==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^5.0.0 + + minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + minimatch@7.4.9: + resolution: {integrity: sha512-Brg/fp/iAVDOQoHxkuN5bEYhyQlZhxddI78yWsCbeEwTHXQjlNLtiJDUsp1GIptVqMI7/gkJMz4vVAc01mpoBw==} + engines: {node: '>=10'} + + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minimizer-webpack-plugin@5.6.1: + resolution: {integrity: sha512-DoeAZz8Q1C1znwsUzej1fdoi4jCf7/+Em27ouLqfK/+3m8G+D7yDhUwrc3CNhjSzGUN1kn7Iv4sWmjflQHenpw==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@minify-html/node': '*' + '@swc/core': '*' + '@swc/css': '*' + '@swc/html': '*' + clean-css: '*' + cssnano: '*' + csso: '*' + esbuild: '*' + html-minifier-terser: '*' + lightningcss: '*' + postcss: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@minify-html/node': + optional: true + '@swc/core': + optional: true + '@swc/css': + optional: true + '@swc/html': + optional: true + clean-css: + optional: true + cssnano: + optional: true + csso: + optional: true + esbuild: + optional: true + html-minifier-terser: + optional: true + lightningcss: + optional: true + postcss: + optional: true + uglify-js: + optional: true + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + mkdirp@2.1.6: + resolution: {integrity: sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==} + engines: {node: '>=10'} + hasBin: true + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + multer@2.2.0: + resolution: {integrity: sha512-6rdyFg2kLrMh9Jee7/BMPuV9lEAd7lLW2YUpF9/YxR7njyoUwwQ0ZPh3TaIY50Sw6vlyD2HW3wGOkTS4P79xrQ==} + engines: {node: '>= 10.16.0'} + + multicast-dns@7.2.5: + resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} + hasBin: true + + nanoid@3.3.16: + resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + napi-postinstall@0.3.4: + resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + needle@3.5.0: + resolution: {integrity: sha512-jaQyPKKk2YokHrEg+vFDYxXIHTCBgiZwSHOoVx/8V3GIBS8/VN6NdVRmg8q1ERtPkMvmOvebsgga4sAj5hls/w==} + engines: {node: '>= 4.4.x'} + hasBin: true + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + negotiator@0.6.4: + resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} + engines: {node: '>= 0.6'} + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + node-abort-controller@3.1.1: + resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} + + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + + node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + + node-releases@2.0.51: + resolution: {integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==} + engines: {node: '>=18'} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + nx@23.0.2: + resolution: {integrity: sha512-e5H6ceqj0Z8ovAmtsiHXswwpiNPrr1DRhvJMTpc2AW8G1za9PKxk3bP5josShsIrmGEsOlBNZZsxszXA2+Q2dw==} + hasBin: true + peerDependencies: + '@swc-node/register': ^1.11.1 + '@swc/core': ^1.15.8 + peerDependenciesMeta: + '@swc-node/register': + optional: true + '@swc/core': + optional: true + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + obuf@1.1.2: + resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + on-headers@1.1.0: + resolution: {integrity: sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + open@10.2.0: + resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} + engines: {node: '>=18'} + + open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + + opener@1.5.2: + resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} + hasBin: true + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-retry@6.2.1: + resolution: {integrity: sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==} + engines: {node: '>=16.17'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-node-version@1.0.1: + resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} + engines: {node: '>= 0.10'} + + parse5@4.0.0: + resolution: {integrity: sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + passport-headerapikey@1.2.2: + resolution: {integrity: sha512-4BvVJRrWsNJPrd3UoZfcnnl4zvUWYKEtfYkoDsaOKBsrWHYmzTApCjs7qUbncOLexE9ul0IRiYBFfBG0y9IVQA==} + + passport-http-bearer@1.0.1: + resolution: {integrity: sha512-SELQM+dOTuMigr9yu8Wo4Fm3ciFfkMq5h/ZQ8ffi4ELgZrX1xh9PlglqZdcUZ1upzJD/whVyt+YWF62s3U6Ipw==} + engines: {node: '>= 0.4.0'} + + passport-strategy@1.0.0: + resolution: {integrity: sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==} + engines: {node: '>= 0.4.0'} + + passport@0.7.0: + resolution: {integrity: sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==} + engines: {node: '>= 0.4.0'} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-to-regexp@0.1.13: + resolution: {integrity: sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==} + + path-to-regexp@8.4.2: + resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pause@0.0.1: + resolution: {integrity: sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + engines: {node: '>=12'} + + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + + pkg-dir@7.0.0: + resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==} + engines: {node: '>=14.16'} + + pkijs@3.4.0: + resolution: {integrity: sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==} + engines: {node: '>=16.0.0'} + + pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + + portfinder@1.0.38: + resolution: {integrity: sha512-rEwq/ZHlJIKw++XtLAO8PPuOQA/zaPJOZJ37BVuN97nLpMJeuDVLVGRwbFoBgLudgdTMP2hdRJP++H+8QOA3vg==} + engines: {node: '>= 10.12'} + + postcss-calc@10.1.1: + resolution: {integrity: sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw==} + engines: {node: ^18.12 || ^20.9 || >=22.0} + peerDependencies: + postcss: ^8.4.38 + + postcss-colormin@7.0.10: + resolution: {integrity: sha512-yFr6JezOolHLta/buLE71VKPh2mXursp4saVe98/ol8ZnEWhL+racShqPKlvd/DKWLre/39B6HhcMXf7RZ3hxg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-convert-values@7.0.12: + resolution: {integrity: sha512-xurKu5qqk4viR3Cp3p4xBR4KfnZm4w4ys6+UBwBmeuBSNkH7+DtLnYOYnOffgtE4yx8sH9S1VZ6RAAvROXzP2Q==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-discard-comments@7.0.8: + resolution: {integrity: sha512-CvvS5S9WrXblFXCEJ9nVo+4z+eA7zSC7Z88V1HEJuwlQhlFnYTIjg1xJY+BCUiG2bvICap2tXii4mP22BD108Q==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-discard-duplicates@7.0.4: + resolution: {integrity: sha512-VBNn1+EuMZkeGVVtz0gRfbNGtx9IFgAsAV+E2pHtXPrp4qfGBkhTIiAuE/wrb+Y6Pakg9NewAlfTpYIFAWODtw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-discard-empty@7.0.3: + resolution: {integrity: sha512-M2pyjQCU+/7cMHVtL6bKTHjv0lZnPLMpicgr67Dlth7AbuV9gjVTtUqaRwn6Pp6BwSDspUzhz8SaUrRykJU5Dw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-discard-overridden@7.0.3: + resolution: {integrity: sha512-aNovXo9UsZuRNLzHJtp13lHIvinDPfiXBPePpXkSjCbgp++iU2FqE+YxvjIsg6EdyPZsASFbfu+JcBFVsErXIQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-import@14.1.0: + resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==} + engines: {node: '>=10.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-loader@8.2.1: + resolution: {integrity: sha512-k98jtRzthjj3f76MYTs9JTpRqV1RaaMhEU0Lpw9OTmQZQdppg4B30VZ74BojuBHt3F4KyubHJoXCMUeM8Bqeow==} + engines: {node: '>= 18.12.0'} + peerDependencies: + '@rspack/core': 0.x || ^1.0.0 || ^2.0.0-0 + postcss: ^7.0.0 || ^8.0.1 + webpack: ^5.0.0 + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true + + postcss-merge-longhand@7.0.7: + resolution: {integrity: sha512-b3mfYUxR388u5Pt0HPcVIUtUDn/k15UfTY9M+ORW+meCR6JLNxoZffiYvXyOYQoRYQNZyX/UFkMCM/mNHxe1qA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-merge-rules@7.0.11: + resolution: {integrity: sha512-SJUPM18g2BmPhf8BVlbwqWz4aK3pLu6u6xjfwEzra7xL6IBR10sUaiB++EzqcVfadPHrKBSMlNdP+XieykhI+Q==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-minify-font-values@7.0.3: + resolution: {integrity: sha512-yilG/VOaNI74IylQvAQQxm3/wZVBkXyYUqNUAdxqwtbWUXPsbK1q8Ms0mL83v+f8YicgcyfYCRZtWACUdYajpA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-minify-gradients@7.0.5: + resolution: {integrity: sha512-YraROyQRg3BI1+Hg8E05B/JPdnTm8EDSVu4P2BxdM+CRiOyfmou809+chGIqo6fQqwjPGQ947nbGncSjmTU1WQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-minify-params@7.0.9: + resolution: {integrity: sha512-R8itbB8BhlpoYyBm1ou0dD+vJnQ3F6adQipR4UnkCHUwlo+S9WXJaDRg1RHjC8YVAtIdrQzSWvJl40HnGDTKjA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-minify-selectors@7.1.2: + resolution: {integrity: sha512-aQtrEWKwqafNlExcKHQvPGsXR2+vlUqqJtf5XsCQcgsSb5PL4wlujWBYDJuWsP4UnQX1YHDHU8qRlD+1PzTQ+Q==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-modules-extract-imports@3.1.0: + resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-local-by-default@4.2.0: + resolution: {integrity: sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-scope@3.2.1: + resolution: {integrity: sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-values@4.0.0: + resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-normalize-charset@7.0.3: + resolution: {integrity: sha512-NoBfZu8PR4c2NlmjvrqQTzCzLY79hwcSRgNQ3ZiNK0ABzf9kYKloE/jNj+/8GQY1wsm8pRRgANk6ydLH8cwo0Q==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-normalize-display-values@7.0.3: + resolution: {integrity: sha512-ldsCX0QIt05pKIOobZtVQ48wXJecr+czw4+e1/YjVhLMqslShgpVxgPtI2CefURR8oyVoYaU/l829MMwExDMLw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-normalize-positions@7.0.4: + resolution: {integrity: sha512-VEvlpeGd3Ju1Hqa/oN4jaP3+ms4laYwkEL9N9u+B6k54PZjXbW1n6wI+aVprf1BQXlCYpS5+1pl/7/vHiKgARg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-normalize-repeat-style@7.0.4: + resolution: {integrity: sha512-6mPKlY/8cSaDHxX502wERADarJsccwlky6yIrOapHH2ZgfoKAV94SbiTKfKEs4EEpdazuc3J72WsqeYk7hp9+Q==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-normalize-string@7.0.3: + resolution: {integrity: sha512-HnEQPUchi1eznmDKEYrKUTqrprEq97SrpUYClgUkv7V2zRODD9DFoUsYU+m9ZOetmD5ku7fEMZB/lwy8IT6xVQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-normalize-timing-functions@7.0.3: + resolution: {integrity: sha512-zmEzHdvpZBZu0OKlbJSfgASQvaayyAoVuWtvyr34IJ/LyS+DaOKvvR3EvFJ9RWWtNIx+CMvO125OVophaxNYew==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-normalize-unicode@7.0.9: + resolution: {integrity: sha512-DRAdWfeh/TjmhLJsw91vdiWCnUod9iwvM7xyS02/nF/sLsCR3A8l3pztrSUrWG8DSBqfX7yEk9FM0USaVJ2mSg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-normalize-url@7.0.3: + resolution: {integrity: sha512-CL93wmloq5qsffmFv+bw24MIRbmhHrp53qoh1LDAb/5TtjWEXI/np4xcP/Gw9oWCb2XyWnqHYLDUwiKRoJBA1Q==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-normalize-whitespace@7.0.3: + resolution: {integrity: sha512-FdHjjn+Ht5Z2ZRjNOmeCbNq6lq09sUYKpmlF/Aq0XjVNSLTL6fmHlA/3swN2wP2caY9GV/tjSDcIIyS7aN7W0A==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-ordered-values@7.0.4: + resolution: {integrity: sha512-nubSi49hDHQk4E8KIj+IbLY8Bg+8OcSUEhgyolgM+atnOvXjV7EjaR6bac4YGZoFyPa9mWoAF3EaYbWdFkKqVg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-reduce-initial@7.0.9: + resolution: {integrity: sha512-ztTNPdIxXTxtBcG03E9u8v44M4ElXbMIRT7pf2onlquGula0Y83nKKxqM22FA/hMgkfCjN7ohevkVlaNwI8iOQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-reduce-transforms@7.0.3: + resolution: {integrity: sha512-FXsnN9ZwcZTT8Yf8cAHA8qIGUXcX6WfLd9JoYhrdDfmvsVhhfqkkv7m4AC3rwFOfz+GzkUa87OCKF9dUcicd+g==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-selector-parser@7.1.4: + resolution: {integrity: sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==} + engines: {node: '>=4'} + + postcss-svgo@7.1.3: + resolution: {integrity: sha512-2QfoFOYMcj8lwcVEf9WeTlkVIAm7u2QvOEhMzkQU3KUhhGX/l8hVV9EtjMv4iq3E9iI3OeeMN0YoMLbGusuigw==} + engines: {node: ^18.12.0 || ^20.9.0 || >= 18} + peerDependencies: + postcss: ^8.5.13 + + postcss-unique-selectors@7.0.7: + resolution: {integrity: sha512-d+sCkaRnSefghOUdH8CMJZV9yUQhj2ojpe8Nw/lA+LV1UOfeleGkLTl6XdCFFSai9UJ+DJPb69FFuqthXYsY8w==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.18: + resolution: {integrity: sha512-xdB1oSLHbz1vRWgCDalrCqEFTWzFlhqFC5tIHLMOSUIjhm3XXQ1qrFy8S/ESr1JYRRXqM3c1QFiMZUJdUTqyMQ==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier@3.9.5: + resolution: {integrity: sha512-/FVl766LpUfB5vXgCYOYa0MeV/441Ia99AeICQIQFTY/Nw0roZwULcXpku5i1/m5kt/baz+s4Zogspd839HSMg==} + engines: {node: '>=14'} + hasBin: true + + pretty-format@30.3.0: + resolution: {integrity: sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + pretty-format@30.4.1: + resolution: {integrity: sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} + + prr@1.0.1: + resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + pure-rand@7.0.1: + resolution: {integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==} + + pvtsutils@1.3.6: + resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} + + pvutils@1.1.5: + resolution: {integrity: sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==} + engines: {node: '>=16.0.0'} + + qs@6.15.3: + resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==} + engines: {node: '>=0.6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + ramda@0.31.3: + resolution: {integrity: sha512-xKADKRNnqmDdX59PPKLm3gGmk1ZgNnj3k7DryqWwkamp4TJ6B36DdpyKEQ0EoEYmH2R62bV4Q+S0ym2z8N2f3Q==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + range-parser@1.3.0: + resolution: {integrity: sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==} + engines: {node: '>= 0.6'} + + raw-body@2.5.3: + resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==} + engines: {node: '>= 0.8'} + + raw-body@3.0.2: + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} + engines: {node: '>= 0.10'} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + react-is@19.2.7: + resolution: {integrity: sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==} + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + + rechoir@0.8.0: + resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} + engines: {node: '>= 10.13.0'} + + reflect-metadata@0.2.2: + resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + + regenerate-unicode-properties@10.2.2: + resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==} + engines: {node: '>=4'} + + regenerate@1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + + regexpu-core@6.4.0: + resolution: {integrity: sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==} + engines: {node: '>=4'} + + regjsgen@0.8.0: + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + + regjsparser@0.13.2: + resolution: {integrity: sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==} + hasBin: true + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve.exports@2.0.3: + resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} + engines: {node: '>=10'} + + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + engines: {node: '>= 0.4'} + hasBin: true + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + + run-applescript@7.1.0: + resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} + engines: {node: '>=18'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sass-embedded-all-unknown@1.100.0: + resolution: {integrity: sha512-auFtXY/kwYILmSVjtBDwyj0axcLbYYiffOKWoaXHnI5bsYwiRbBh3EneR1rpbX2ZIZCrwX93i5pxKLTZF/662Q==} + cpu: ['!arm', '!arm64', '!riscv64', '!x64'] + + sass-embedded-android-arm64@1.100.0: + resolution: {integrity: sha512-W+Ru9JwTnfU0UX3jSZcbqFdtKFMcYdfFwytc57h2DgnqCOIiAqI2E06mABZBZC+r3LwXCBuS5GbXAGeVgvVDkA==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [android] + + sass-embedded-android-arm@1.100.0: + resolution: {integrity: sha512-70f3HgX2pFNmzpGQ86n5e6QfWn2fP4QUQGfFQK0P1XH73ZLIzLo2YqygrGKGKeeqtc5eU2Wl1/xQzhzuKnO4kw==} + engines: {node: '>=14.0.0'} + cpu: [arm] + os: [android] + + sass-embedded-android-riscv64@1.100.0: + resolution: {integrity: sha512-icU3o0V/uCSytSpf+tX5Lf51BvyQEbLzDUJfUi9etSauYBGHpPKkdtdZH0si4v98phq11Kl8rSV1SggksxF1Hg==} + engines: {node: '>=14.0.0'} + cpu: [riscv64] + os: [android] + + sass-embedded-android-x64@1.100.0: + resolution: {integrity: sha512-mevF9VQk6gEYByy8+jusaHGmd7Usb2ytX/DsEOd0JtOGCtcf1kh575xJ6OUBDIcJ15uLnbau/0iy1eP6WVBvWA==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [android] + + sass-embedded-darwin-arm64@1.100.0: + resolution: {integrity: sha512-1PVlYi61POo93IT/FfrG1mc1tAHxeSTyUALF2aOFmXGWjVXr3bQzEQiBGCOvQbj/ix+5hNyXFXcEMEyKvtUJJA==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [darwin] + + sass-embedded-darwin-x64@1.100.0: + resolution: {integrity: sha512-x97o3JnGyImZNCIVs9wQHJUE5QCvmVIKaH1cwrz/5dK7OT1FpeNiW+u9TUomP9hG6Ekjd8EL8NBHpxTfIhdjmg==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [darwin] + + sass-embedded-linux-arm64@1.100.0: + resolution: {integrity: sha512-Dwjmj8Z6VRy7rAi53JAdEwIyUjpfl7PhpSc2/LpQPQx+aO5Dp7Spaipkax0ufJl1SoDUdchCsM4y/88YaluorQ==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [linux] + libc: glibc + + sass-embedded-linux-arm@1.100.0: + resolution: {integrity: sha512-9Ul7O1eKrc5YlhwWjkp8tZPSe3UEwSZ1uwUZOQom1HL0pRlBA6F/IlGZYFTLwnHMIP1fc77MMNaBRfc05mKMpw==} + engines: {node: '>=14.0.0'} + cpu: [arm] + os: [linux] + libc: glibc + + sass-embedded-linux-musl-arm64@1.100.0: + resolution: {integrity: sha512-XpACJB2KjSLjf2e9uuvGVdOURsoNrFqgRiihhXyUHK9W0t3LIHb7z5MA/7XGPIT9bWSOO2zyw+rH/FHtDV/Yrg==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [linux] + libc: musl + + sass-embedded-linux-musl-arm@1.100.0: + resolution: {integrity: sha512-sl0JgbGloPyJg66XXx5UDSDScZ0oU85DpMQU4JU/sCUCFj1Z8zZ69SJWKTCNE4/jwnce7WI2zPCV5AG+RHOZJw==} + engines: {node: '>=14.0.0'} + cpu: [arm] + os: [linux] + libc: musl + + sass-embedded-linux-musl-riscv64@1.100.0: + resolution: {integrity: sha512-ShvI0Kx04mwoCARwZ0UjiT97isQvzO80tAt91zmFyHLN9kelc/IrQi940farSm2xQVPCKdeVyeG0ekBsokSpYQ==} + engines: {node: '>=14.0.0'} + cpu: [riscv64] + os: [linux] + libc: musl + + sass-embedded-linux-musl-x64@1.100.0: + resolution: {integrity: sha512-TDBCRWNuS4RDLQXvRc1gjZlWiWTWaWGp0Bwu/IKwJxov81lsvrCs3TihTyNXtW7V5aoN4Ky3r0QOkNb3mwmBnA==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [linux] + libc: musl + + sass-embedded-linux-riscv64@1.100.0: + resolution: {integrity: sha512-j4ENJGOheO+fm3j/yorLxCjBP6/XskrZx7dTLlT+lXYwN/qqCqoA/gsNLI0McS3DFM6GBwPiffzWsdWS8t6sEQ==} + engines: {node: '>=14.0.0'} + cpu: [riscv64] + os: [linux] + libc: glibc + + sass-embedded-linux-x64@1.100.0: + resolution: {integrity: sha512-0vUSN8j0WGtCJIOPh//EmUvYGHW0QOe5iul8qyhPk50MAcw49MA0r34AhftjDdx94ILPF6vApFs0gwHPQRlpVA==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [linux] + libc: glibc + + sass-embedded-unknown-all@1.100.0: + resolution: {integrity: sha512-c+naBgWId4MIpToXcI0DgqetjdAkwTTAxFAuOaBz7HUXLdyG1oZRrEvSsbe41nEdQOKH0vgofVFCeSQgoXOG9A==} + os: ['!android', '!darwin', '!linux', '!win32'] + + sass-embedded-win32-arm64@1.100.0: + resolution: {integrity: sha512-iE+yxj+hUXwwbqpHkXxgAWTzeRfcWxJ7SSTQEPMk48lwq3oCrWLlz5sQuWHbuTK/i0GKQfROdP+hOmPi89yjUg==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [win32] + + sass-embedded-win32-x64@1.100.0: + resolution: {integrity: sha512-qI4F8MI7/KYoy9NdjJfhSspG42WPkADSNDvwEV7qWvCSFC83koJssRsKO2/PfY+niZz6BG65Ic/D+A11h959hw==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [win32] + + sass-embedded@1.100.0: + resolution: {integrity: sha512-Ut8wlQSk19tm7jMK6mz6cF1+e+E7tUnW2tM02zQDPnOTcVbV8qCQG8UWxZkkNlY50+hV3hqP24OOkUlMz8xBpw==} + engines: {node: '>=16.0.0'} + hasBin: true + + sass-loader@16.0.8: + resolution: {integrity: sha512-hcov4ZwZJIGbEuyNr9EmiTmZueyrxSToE6GOzoZnq5JM7ecRO7ttyvilPn+VmRsqiP16+VYZzVnGZj/hzZgKBA==} + engines: {node: '>= 18.12.0'} + peerDependencies: + '@rspack/core': 0.x || ^1.0.0 || ^2.0.0-0 + node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + sass: ^1.3.0 + sass-embedded: '*' + webpack: ^5.0.0 + peerDependenciesMeta: + '@rspack/core': + optional: true + node-sass: + optional: true + sass: + optional: true + sass-embedded: + optional: true + webpack: + optional: true + + sass@1.100.0: + resolution: {integrity: sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ==} + engines: {node: '>=20.19.0'} + hasBin: true + + sass@1.101.0: + resolution: {integrity: sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==} + engines: {node: '>=20.19.0'} + hasBin: true + + sax@1.6.0: + resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} + engines: {node: '>=11.0.0'} + + schema-utils@3.3.0: + resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} + engines: {node: '>= 10.13.0'} + + schema-utils@4.3.3: + resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} + engines: {node: '>= 10.13.0'} + + secure-compare@3.0.1: + resolution: {integrity: sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==} + + select-hose@2.0.0: + resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==} + + selfsigned@5.5.0: + resolution: {integrity: sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew==} + engines: {node: '>=18'} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + send@0.19.2: + resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} + engines: {node: '>= 0.8.0'} + + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} + + serialize-javascript@7.0.7: + resolution: {integrity: sha512-YAy8Od6KV+uuwUuU50np8fGB/Aues6Y0nAhA9y/hId74PlKUcme4pXcBD46NWKr1Q4osN/iseZ17YqO1XfmI8g==} + engines: {node: '>=20.0.0'} + + serve-index@1.9.2: + resolution: {integrity: sha512-KDj11HScOaLmrPxl70KYNW1PksP4Nb/CLL2yvC+Qd2kHMPEEpfc4Re2e4FOay+bC/+XQl/7zAcWON3JVo5v3KQ==} + engines: {node: '>= 0.8.0'} + + serve-static@1.16.3: + resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} + engines: {node: '>= 0.8.0'} + + serve-static@2.2.1: + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + engines: {node: '>= 18'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shallow-clone@3.0.1: + resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} + engines: {node: '>=8'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-exec@1.0.2: + resolution: {integrity: sha512-jyVd+kU2X+mWKMmGhx4fpWbPsjvD53k9ivqetutVW/BQ+WIZoDoP4d8vUMGezV6saZsiNoW2f9GIhg9Dondohg==} + + shell-quote@1.10.0: + resolution: {integrity: sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==} + engines: {node: '>= 0.4'} + + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.1: + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slice-ansi@7.1.2: + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} + engines: {node: '>=18'} + + slice-ansi@8.0.0: + resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} + engines: {node: '>=20'} + + smol-toml@1.6.1: + resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==} + engines: {node: '>= 18'} + + sockjs@0.3.24: + resolution: {integrity: sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-loader@5.0.0: + resolution: {integrity: sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==} + engines: {node: '>= 18.12.0'} + peerDependencies: + webpack: ^5.72.1 + + source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + + source-map-support@0.5.19: + resolution: {integrity: sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + spdy-transport@3.0.0: + resolution: {integrity: sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==} + + spdy@4.0.2: + resolution: {integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==} + engines: {node: '>=6.0.0'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + + statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string-width@8.2.2: + resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==} + engines: {node: '>=20'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strtok3@10.3.5: + resolution: {integrity: sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==} + engines: {node: '>=18'} + + style-loader@3.3.4: + resolution: {integrity: sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^5.0.0 + + stylehacks@7.0.11: + resolution: {integrity: sha512-iODNfhXVLqc5LADs+Y6Oh5wJuK5ZcHbVng8aiK3y9pjMQdc5hLrBW0eFU6FtnpNrE6PoEg/MmFTU4waotj5WNg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.5.13 + + supabase@2.109.1: + resolution: {integrity: sha512-N2yP2MHTxOxXBWhfn3poudpJn4pkPosAUo7J/46FTou/l7wOwFi9tox8NSN6HljWkfM0zhwPRimNNGC9XBMoxQ==} + hasBin: true + + superagent@10.3.0: + resolution: {integrity: sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==} + engines: {node: '>=14.18.0'} + + supertest@7.2.2: + resolution: {integrity: sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==} + engines: {node: '>=14.18.0'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svgo@4.0.2: + resolution: {integrity: sha512-ekx94z1rRc5LDi6oSUaeRnYhd0UOJxdtQCL2rF8xpWxD3TPAsISWOrxezqGovqS38GRZOdpDfvQe3ts6F7nsng==} + engines: {node: '>=16'} + hasBin: true + + sync-child-process@1.0.2: + resolution: {integrity: sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==} + engines: {node: '>=16.0.0'} + + sync-message-port@1.2.0: + resolution: {integrity: sha512-gAQ9qrUN/UCypHtGFbbe7Rc/f9bzO88IwrG8TDo/aMKAApKyD6E3W4Cm0EfhfBb6Z6SKt59tTCTfD+n1xmAvMg==} + engines: {node: '>=16.0.0'} + + synckit@0.11.13: + resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==} + engines: {node: ^14.18.0 || >=16.0.0} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + + tcp-port-used@1.0.3: + resolution: {integrity: sha512-4CEQ3qRJYo+mtEbJ+OoQu3dF4TDkwaO3RDVC4UzP5cpAOIUWwuwPjD7sdxDFFqsMUjsXVVYBMlg/boAaloThMA==} + + terser-webpack-plugin@5.6.1: + resolution: {integrity: sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@minify-html/node': '*' + '@swc/core': '*' + '@swc/css': '*' + '@swc/html': '*' + clean-css: '*' + cssnano: '*' + csso: '*' + esbuild: '*' + html-minifier-terser: '*' + lightningcss: '*' + postcss: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@minify-html/node': + optional: true + '@swc/core': + optional: true + '@swc/css': + optional: true + '@swc/html': + optional: true + clean-css: + optional: true + cssnano: + optional: true + csso: + optional: true + esbuild: + optional: true + html-minifier-terser: + optional: true + lightningcss: + optional: true + postcss: + optional: true + uglify-js: + optional: true + + terser@5.49.0: + resolution: {integrity: sha512-SNiDnXyHSrxVcIOtVbULzcTmniUiwcV7Nwdyj1twVubeTmbjoa8p69KKDpfkdoOavuM4/GRm1+ykI8qqnavHoA==} + engines: {node: '>=10'} + hasBin: true + + test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + + thingies@2.6.0: + resolution: {integrity: sha512-rMHRjmlFLM1R96UYPvpmnc3LYtdFrT33JIB7L9hetGue1qAPfn1N2LJeEjxUSidu1Iku+haLZXDuEXUHNGO/lg==} + engines: {node: '>=10.18'} + peerDependencies: + tslib: ^2 + + thunky@1.1.0: + resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} + + tinyexec@1.2.4: + resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tmp@0.2.7: + resolution: {integrity: sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==} + engines: {node: '>=14.14'} + + tmpl@1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + token-types@6.1.2: + resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==} + engines: {node: '>=14.16'} + + tree-dump@1.1.0: + resolution: {integrity: sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + ts-jest@29.4.11: + resolution: {integrity: sha512-IrFl7l9AuB/qrNw5quqvAv/hmKMb8dhWOH4jQOGo0Oq8tCeo1O86/iTFG1FaRimgUkF13l4PcepO8ATFT6Ns4g==} + engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/transform': ^29.0.0 || ^30.0.0 + '@jest/types': ^29.0.0 || ^30.0.0 + babel-jest: ^29.0.0 || ^30.0.0 + esbuild: '*' + jest: ^29.0.0 || ^30.0.0 + jest-util: ^29.0.0 || ^30.0.0 + typescript: '>=4.3 <7' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/transform': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + jest-util: + optional: true + + ts-loader@9.6.2: + resolution: {integrity: sha512-R4iuczmtgxvtuI556s+hTZ6/7Ee03VCAk/l/M8LY1OAsUgB7YydsCxkgq9D9pKRaD7GJqUi2u8fp9zZP/ufjKA==} + engines: {node: '>=12.0.0'} + peerDependencies: + loader-utils: '*' + typescript: '*' + webpack: ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + loader-utils: + optional: true + + ts-morph@18.0.0: + resolution: {integrity: sha512-Kg5u0mk19PIIe4islUI/HWRvm9bC1lHejK4S0oh1zaZ77TMZAEmQC0sHQYiu2RgCQFZKXz1fMVi/7nOOeirznA==} + + ts-node@10.9.1: + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + + ts-toolbelt@9.6.0: + resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==} + + tsconfig-paths-webpack-plugin@4.2.0: + resolution: {integrity: sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==} + engines: {node: '>=10.13.0'} + + tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsyringe@4.10.0: + resolution: {integrity: sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==} + engines: {node: '>= 6.0.0'} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + type-is@2.1.0: + resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} + engines: {node: '>= 18'} + + typed-assert@1.0.9: + resolution: {integrity: sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==} + + typedarray@0.0.6: + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + + types-ramda@0.31.0: + resolution: {integrity: sha512-vaoC35CRC3xvL8Z6HkshDbi6KWM1ezK0LHN0YyxXWUn9HKzBNg/T3xSGlJZjCYspnOD3jE7bcizsp0bUXZDxnQ==} + + typescript-eslint@8.63.0: + resolution: {integrity: sha512-xgwXyzG4sK9ALkBxbyGkTMMOS+imnW65iPhxCQMK83KhxyoDNW7l+IDqEf9vMdoUidHpOoS967RCq4eMiTexwQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + uglify-js@3.19.3: + resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} + engines: {node: '>=0.8.0'} + hasBin: true + + uid@2.0.2: + resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} + engines: {node: '>=8'} + + uint8array-extras@1.5.0: + resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} + engines: {node: '>=18'} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} + engines: {node: '>=4'} + + unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + + unicode-match-property-value-ecmascript@2.2.1: + resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==} + engines: {node: '>=4'} + + unicode-property-aliases-ecmascript@2.2.0: + resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} + engines: {node: '>=4'} + + union@0.5.0: + resolution: {integrity: sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==} + engines: {node: '>= 0.8.0'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + unrs-resolver@1.12.2: + resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + url-join@4.0.1: + resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). + hasBin: true + + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + + v8-to-istanbul@9.3.0: + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} + engines: {node: '>=10.12.0'} + + validator@13.15.35: + resolution: {integrity: sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==} + engines: {node: '>= 0.10'} + + varint@6.0.0: + resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + walker@1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + + watchpack@2.5.2: + resolution: {integrity: sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==} + engines: {node: '>=10.13.0'} + + wbuf@1.7.3: + resolution: {integrity: sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==} + + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + + webpack-cli@7.2.1: + resolution: {integrity: sha512-YwSGbcZdfz12DM8JIseVPr3oBb09IgVCVc4vY3oDvZnI/mALTGPAP1QiqOi4/bBLSJrRHaqDIXeHcNA0+G38aw==} + engines: {node: '>=20.9.0'} + hasBin: true + peerDependencies: + js-yaml: ^4.0.0 || ^5.0.0 + json5: ^2.2.3 + toml: ^3.0.0 || ^4.0.0 + webpack: ^5.101.0 + webpack-bundle-analyzer: ^4.0.0 || ^5.0.0 + webpack-dev-server: ^5.0.0 || ^6.0.0 + peerDependenciesMeta: + js-yaml: + optional: true + json5: + optional: true + toml: + optional: true + webpack-bundle-analyzer: + optional: true + webpack-dev-server: + optional: true + + webpack-dev-middleware@7.4.5: + resolution: {integrity: sha512-uxQ6YqGdE4hgDKNf7hUiPXOdtkXvBJXrfEGYSx7P7LC8hnUYGK70X6xQXUvXeNyBDDcsiQXpG2m3G9vxowaEuA==} + engines: {node: '>= 18.12.0'} + peerDependencies: + webpack: ^5.0.0 + peerDependenciesMeta: + webpack: + optional: true + + webpack-dev-server@5.2.6: + resolution: {integrity: sha512-HNLRmamRvVavZQ+avceZifmv8hmdUjg43t6MI4SqJDwFdW7RPQwH5vzGhDRZSX59SgfbeHhLnq3g+uooWo7pVw==} + engines: {node: '>= 18.12.0'} + hasBin: true + peerDependencies: + webpack: ^5.0.0 + webpack-cli: '*' + peerDependenciesMeta: + webpack: + optional: true + webpack-cli: + optional: true + + webpack-merge@6.0.1: + resolution: {integrity: sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==} + engines: {node: '>=18.0.0'} + + webpack-node-externals@3.0.0: + resolution: {integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==} + engines: {node: '>=6'} + + webpack-sources@3.5.1: + resolution: {integrity: sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw==} + engines: {node: '>=10.13.0'} + + webpack-subresource-integrity@5.1.0: + resolution: {integrity: sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==} + engines: {node: '>= 12'} + peerDependencies: + html-webpack-plugin: '>= 5.0.0-beta.1 < 6' + webpack: ^5.12.0 + peerDependenciesMeta: + html-webpack-plugin: + optional: true + + webpack@5.108.4: + resolution: {integrity: sha512-yur8LyJoeiWh47dErD+Ok7vlbmDsJ3UbbRPAoxbGJ54WpE2y5yVo5G/inUzujnYgw3tPmBRdn+G7PoxXaYC33w==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + + websocket-driver@0.7.5: + resolution: {integrity: sha512-ZL2+3c7kMBdIRCMz6l8jQMHyGVxj+UL+xVk74Ombiciboca8rHa15L86B19E5oh1pL9Ii/uj54gtsIrZGMo6zA==} + engines: {node: '>=0.8.0'} + + websocket-extensions@0.1.4: + resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} + engines: {node: '>=0.8.0'} + + whatwg-encoding@2.0.0: + resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} + engines: {node: '>=12'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + which@3.0.1: + resolution: {integrity: sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + wildcard@2.0.1: + resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + + wrap-ansi@10.0.0: + resolution: {integrity: sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==} + engines: {node: '>=20'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@5.0.1: + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + 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 + + wsl-utils@0.1.0: + resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} + engines: {node: '>=18'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml@1.10.3: + resolution: {integrity: sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==} + engines: {node: '>= 6'} + + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yargs@17.7.3: + resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==} + engines: {node: '>=12'} + + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yocto-queue@1.2.2: + resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} + engines: {node: '>=12.20'} + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + +snapshots: + + '@angular-devkit/core@19.2.24(chokidar@4.0.3)': + dependencies: + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) + jsonc-parser: 3.3.1 + picomatch: 4.0.4 + rxjs: 7.8.1 + source-map: 0.7.4 + optionalDependencies: + chokidar: 4.0.3 + + '@angular-devkit/schematics@19.2.24(chokidar@4.0.3)': + dependencies: + '@angular-devkit/core': 19.2.24(chokidar@4.0.3) + jsonc-parser: 3.3.1 + magic-string: 0.30.17 + ora: 5.4.1 + rxjs: 7.8.1 + transitivePeerDependencies: + - chokidar + + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3(supports-color@7.2.0) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.7': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.6 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + '@babel/traverse': 7.29.7 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + regexpu-core: 6.4.0 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + debug: 4.4.3(supports-color@7.2.0) + lodash.debounce: 4.0.8 + resolve: 1.22.12 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-member-expression-to-functions@7.29.7': + dependencies: + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/helper-plugin-utils@7.29.7': {} + + '@babel/helper-remap-async-to-generator@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-wrap-function': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + dependencies: + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helper-wrap-function@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/parser@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-decorators@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-syntax-decorators': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-decorators@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-import-assertions@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-import-attributes@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-arrow-functions@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-async-generator-functions@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.7) + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoped-functions@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-block-scoping@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-class-properties@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/template': 7.29.7 + + '@babel/plugin-transform-destructuring@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-dotall-regex@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-duplicate-keys@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-dynamic-import@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-explicit-resource-management@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-exponentiation-operator@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-export-namespace-from@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-for-of@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-json-strings@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-literals@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-logical-assignment-operators@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-member-expression-literals@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-modules-amd@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-systemjs@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-umd@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-new-target@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-nullish-coalescing-operator@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-numeric-separator@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-object-rest-spread@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7) + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-object-super@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-catch-binding@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-optional-chaining@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-parameters@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-private-methods@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-property-in-object@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-property-literals@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-regenerator@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-regexp-modifiers@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-reserved-words@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.7) + babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.29.7) + babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.7) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-shorthand-properties@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-spread@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-sticky-regex@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-template-literals@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-typeof-symbol@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-unicode-escapes@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-unicode-property-regex@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-unicode-regex@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-unicode-sets-regex@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/preset-env@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/core': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.7) + '@babel/plugin-syntax-import-assertions': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-syntax-import-attributes': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.29.7) + '@babel/plugin-transform-arrow-functions': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-async-generator-functions': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-async-to-generator': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-block-scoped-functions': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-block-scoping': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-class-static-block': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-computed-properties': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-dotall-regex': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-duplicate-keys': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-dynamic-import': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-explicit-resource-management': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-exponentiation-operator': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-export-namespace-from': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-for-of': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-function-name': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-json-strings': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-literals': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-logical-assignment-operators': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-member-expression-literals': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-modules-amd': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-modules-systemjs': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-modules-umd': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-new-target': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-numeric-separator': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-object-rest-spread': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-object-super': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-optional-catch-binding': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-private-property-in-object': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-property-literals': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-regenerator': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-regexp-modifiers': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-reserved-words': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-shorthand-properties': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-spread': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-sticky-regex': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-template-literals': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-typeof-symbol': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-unicode-escapes': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-unicode-property-regex': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-unicode-sets-regex': 7.29.7(@babel/core@7.29.7) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.29.7) + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.7) + babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@7.29.7) + babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.7) + core-js-compat: 3.49.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/types': 7.29.7 + esutils: 2.0.3 + + '@babel/preset-typescript@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + '@babel/runtime@7.29.7': {} + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/traverse@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + debug: 4.4.3(supports-color@7.2.0) + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@bcoe/v8-coverage@0.2.3': {} + + '@borewit/text-codec@0.2.2': {} + + '@bufbuild/protobuf@2.12.1': {} + + '@colordx/core@5.5.0': {} + + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + + '@discoveryjs/json-ext@1.1.0': {} + + '@ecies/ciphers@0.2.6(@noble/ciphers@1.3.0)': + dependencies: + '@noble/ciphers': 1.3.0 + + '@emnapi/core@1.10.0': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true + + '@emnapi/core@1.4.5': + dependencies: + '@emnapi/wasi-threads': 1.0.4 + tslib: 2.8.1 + + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.4.5': + dependencies: + tslib: 2.8.1 + + '@emnapi/wasi-threads@1.0.4': + dependencies: + tslib: 2.8.1 + + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.5(jiti@2.7.0))': + dependencies: + eslint: 9.39.5(jiti@2.7.0) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.2': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3(supports-color@7.2.0) + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.6': + dependencies: + ajv: 6.15.0 + debug: 4.4.3(supports-color@7.2.0) + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.3.0 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.5': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.2.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@istanbuljs/load-nyc-config@1.1.0': + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.15.0 + resolve-from: 5.0.0 + + '@istanbuljs/schema@0.1.6': {} + + '@jest/console@30.3.0': + dependencies: + '@jest/types': 30.3.0 + '@types/node': 22.20.1 + chalk: 4.1.2 + jest-message-util: 30.3.0 + jest-util: 30.3.0 + slash: 3.0.0 + + '@jest/console@30.4.1': + dependencies: + '@jest/types': 30.4.1 + '@types/node': 22.20.1 + chalk: 4.1.2 + jest-message-util: 30.4.1 + jest-util: 30.4.1 + slash: 3.0.0 + + '@jest/core@30.3.0(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3))': + dependencies: + '@jest/console': 30.3.0 + '@jest/pattern': 30.0.1 + '@jest/reporters': 30.3.0 + '@jest/test-result': 30.3.0 + '@jest/transform': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 22.20.1 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 4.4.0 + exit-x: 0.2.2 + graceful-fs: 4.2.11 + jest-changed-files: 30.3.0 + jest-config: 30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)) + jest-haste-map: 30.3.0 + jest-message-util: 30.3.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.3.0 + jest-resolve-dependencies: 30.3.0 + jest-runner: 30.3.0 + jest-runtime: 30.3.0 + jest-snapshot: 30.3.0 + jest-util: 30.3.0 + jest-validate: 30.3.0 + jest-watcher: 30.3.0 + pretty-format: 30.3.0 + slash: 3.0.0 + transitivePeerDependencies: + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + + '@jest/create-cache-key-function@30.4.1': + dependencies: + '@jest/types': 30.4.1 + + '@jest/diff-sequences@30.0.1': {} + + '@jest/diff-sequences@30.3.0': {} + + '@jest/diff-sequences@30.4.0': {} + + '@jest/environment@30.3.0': + dependencies: + '@jest/fake-timers': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 22.20.1 + jest-mock: 30.3.0 + + '@jest/environment@30.4.1': + dependencies: + '@jest/fake-timers': 30.4.1 + '@jest/types': 30.4.1 + '@types/node': 22.20.1 + jest-mock: 30.4.1 + + '@jest/expect-utils@30.3.0': + dependencies: + '@jest/get-type': 30.1.0 + + '@jest/expect-utils@30.4.1': + dependencies: + '@jest/get-type': 30.1.0 + + '@jest/expect@30.3.0': + dependencies: + expect: 30.3.0 + jest-snapshot: 30.3.0 + transitivePeerDependencies: + - supports-color + + '@jest/expect@30.4.1': + dependencies: + expect: 30.4.1 + jest-snapshot: 30.4.1 + transitivePeerDependencies: + - supports-color + + '@jest/fake-timers@30.3.0': + dependencies: + '@jest/types': 30.3.0 + '@sinonjs/fake-timers': 15.4.0 + '@types/node': 22.20.1 + jest-message-util: 30.3.0 + jest-mock: 30.3.0 + jest-util: 30.3.0 + + '@jest/fake-timers@30.4.1': + dependencies: + '@jest/types': 30.4.1 + '@sinonjs/fake-timers': 15.4.0 + '@types/node': 22.20.1 + jest-message-util: 30.4.1 + jest-mock: 30.4.1 + jest-util: 30.4.1 + + '@jest/get-type@30.1.0': {} + + '@jest/globals@30.3.0': + dependencies: + '@jest/environment': 30.3.0 + '@jest/expect': 30.3.0 + '@jest/types': 30.3.0 + jest-mock: 30.3.0 + transitivePeerDependencies: + - supports-color + + '@jest/globals@30.4.1': + dependencies: + '@jest/environment': 30.4.1 + '@jest/expect': 30.4.1 + '@jest/types': 30.4.1 + jest-mock: 30.4.1 + transitivePeerDependencies: + - supports-color + + '@jest/pattern@30.0.1': + dependencies: + '@types/node': 22.20.1 + jest-regex-util: 30.0.1 + + '@jest/pattern@30.4.0': + dependencies: + '@types/node': 22.20.1 + jest-regex-util: 30.4.0 + + '@jest/reporters@30.3.0': + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 30.3.0 + '@jest/test-result': 30.3.0 + '@jest/transform': 30.3.0 + '@jest/types': 30.3.0 + '@jridgewell/trace-mapping': 0.3.31 + '@types/node': 22.20.1 + chalk: 4.1.2 + collect-v8-coverage: 1.0.3 + exit-x: 0.2.2 + glob: 10.5.0 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.2.0 + jest-message-util: 30.3.0 + jest-util: 30.3.0 + jest-worker: 30.3.0 + slash: 3.0.0 + string-length: 4.0.2 + v8-to-istanbul: 9.3.0 + transitivePeerDependencies: + - supports-color + + '@jest/reporters@30.4.1': + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 30.4.1 + '@jest/test-result': 30.4.1 + '@jest/transform': 30.4.1 + '@jest/types': 30.4.1 + '@jridgewell/trace-mapping': 0.3.31 + '@types/node': 22.20.1 + chalk: 4.1.2 + collect-v8-coverage: 1.0.3 + exit-x: 0.2.2 + glob: 10.5.0 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.2.0 + jest-message-util: 30.4.1 + jest-util: 30.4.1 + jest-worker: 30.4.1 + slash: 3.0.0 + string-length: 4.0.2 + v8-to-istanbul: 9.3.0 + transitivePeerDependencies: + - supports-color + + '@jest/schemas@30.0.5': + dependencies: + '@sinclair/typebox': 0.34.52 + + '@jest/schemas@30.4.1': + dependencies: + '@sinclair/typebox': 0.34.52 + + '@jest/snapshot-utils@30.3.0': + dependencies: + '@jest/types': 30.3.0 + chalk: 4.1.2 + graceful-fs: 4.2.11 + natural-compare: 1.4.0 + + '@jest/snapshot-utils@30.4.1': + dependencies: + '@jest/types': 30.4.1 + chalk: 4.1.2 + graceful-fs: 4.2.11 + natural-compare: 1.4.0 + + '@jest/source-map@30.0.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + callsites: 3.1.0 + graceful-fs: 4.2.11 + + '@jest/test-result@30.3.0': + dependencies: + '@jest/console': 30.3.0 + '@jest/types': 30.3.0 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.3 + + '@jest/test-result@30.4.1': + dependencies: + '@jest/console': 30.4.1 + '@jest/types': 30.4.1 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.3 + + '@jest/test-sequencer@30.3.0': + dependencies: + '@jest/test-result': 30.3.0 + graceful-fs: 4.2.11 + jest-haste-map: 30.3.0 + slash: 3.0.0 + + '@jest/test-sequencer@30.4.1': + dependencies: + '@jest/test-result': 30.4.1 + graceful-fs: 4.2.11 + jest-haste-map: 30.4.1 + slash: 3.0.0 + + '@jest/transform@30.3.0': + dependencies: + '@babel/core': 7.29.7 + '@jest/types': 30.3.0 + '@jridgewell/trace-mapping': 0.3.31 + babel-plugin-istanbul: 7.0.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 30.3.0 + jest-regex-util: 30.0.1 + jest-util: 30.3.0 + pirates: 4.0.7 + slash: 3.0.0 + write-file-atomic: 5.0.1 + transitivePeerDependencies: + - supports-color + + '@jest/transform@30.4.1': + dependencies: + '@babel/core': 7.29.7 + '@jest/types': 30.4.1 + '@jridgewell/trace-mapping': 0.3.31 + babel-plugin-istanbul: 7.0.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 30.4.1 + jest-regex-util: 30.4.0 + jest-util: 30.4.1 + pirates: 4.0.7 + slash: 3.0.0 + write-file-atomic: 5.0.1 + transitivePeerDependencies: + - supports-color + + '@jest/types@30.3.0': + dependencies: + '@jest/pattern': 30.0.1 + '@jest/schemas': 30.0.5 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 22.20.1 + '@types/yargs': 17.0.35 + chalk: 4.1.2 + + '@jest/types@30.4.1': + dependencies: + '@jest/pattern': 30.4.0 + '@jest/schemas': 30.4.1 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 22.20.1 + '@types/yargs': 17.0.35 + chalk: 4.1.2 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@jsonjoy.com/base64@1.1.2(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/base64@17.67.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/buffers@1.2.1(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/buffers@17.67.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/codegen@1.0.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/codegen@17.67.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/fs-core@4.64.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-node-builtins': 4.64.0(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.64.0(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-fsa@4.64.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-core': 4.64.0(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.64.0(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.64.0(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-node-builtins@4.64.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/fs-node-to-fsa@4.64.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-fsa': 4.64.0(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.64.0(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.64.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-node-utils@4.64.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-node-builtins': 4.64.0(tslib@2.8.1) + glob-to-regex.js: 1.2.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-node@4.64.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-core': 4.64.0(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.64.0(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.64.0(tslib@2.8.1) + '@jsonjoy.com/fs-print': 4.64.0(tslib@2.8.1) + '@jsonjoy.com/fs-snapshot': 4.64.0(tslib@2.8.1) + glob-to-regex.js: 1.2.0(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-print@4.64.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-node-utils': 4.64.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-snapshot@4.64.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.64.0(tslib@2.8.1) + '@jsonjoy.com/json-pack': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pack@1.21.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/base64': 1.1.2(tslib@2.8.1) + '@jsonjoy.com/buffers': 1.2.1(tslib@2.8.1) + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + '@jsonjoy.com/json-pointer': 1.0.2(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + hyperdyperid: 1.2.0 + thingies: 2.6.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pack@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/base64': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/codegen': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/json-pointer': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + hyperdyperid: 1.2.0 + thingies: 2.6.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pointer@1.0.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pointer@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/util@1.9.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 1.2.1(tslib@2.8.1) + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/util@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/codegen': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + + '@leichtgewicht/ip-codec@2.0.5': {} + + '@lukeed/csprng@1.1.0': {} + + '@napi-rs/wasm-runtime@0.2.4': + dependencies: + '@emnapi/core': 1.4.5 + '@emnapi/runtime': 1.4.5 + '@tybys/wasm-util': 0.9.0 + + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@nestjs/axios@4.0.1(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.18.1)(rxjs@7.8.2)': + dependencies: + '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + axios: 1.18.1 + rxjs: 7.8.2 + + '@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + dependencies: + file-type: 21.3.4 + iterare: 1.2.1 + load-esm: 1.0.3 + reflect-metadata: 0.2.2 + rxjs: 7.8.2 + tslib: 2.8.1 + uid: 2.0.2 + optionalDependencies: + class-transformer: 0.5.1 + class-validator: 0.15.1 + transitivePeerDependencies: + - supports-color + + '@nestjs/core@11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + dependencies: + '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + fast-safe-stringify: 2.1.1 + iterare: 1.2.1 + path-to-regexp: 8.4.2 + reflect-metadata: 0.2.2 + rxjs: 7.8.2 + tslib: 2.8.1 + uid: 2.0.2 + optionalDependencies: + '@nestjs/platform-express': 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28) + + '@nestjs/passport@11.0.5(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0)': + dependencies: + '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + passport: 0.7.0 + + '@nestjs/platform-express@11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)': + dependencies: + '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + cors: 2.8.6 + express: 5.2.1 + multer: 2.2.0 + path-to-regexp: 8.4.2 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@nestjs/schedule@6.1.3(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)': + dependencies: + '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + cron: 4.4.0 + + '@nestjs/schematics@11.1.0(chokidar@4.0.3)(prettier@3.9.5)(typescript@5.9.3)': + dependencies: + '@angular-devkit/core': 19.2.24(chokidar@4.0.3) + '@angular-devkit/schematics': 19.2.24(chokidar@4.0.3) + comment-json: 5.0.0 + jsonc-parser: 3.3.1 + pluralize: 8.0.0 + typescript: 5.9.3 + optionalDependencies: + prettier: 3.9.5 + transitivePeerDependencies: + - chokidar + + '@nestjs/testing@11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(@nestjs/platform-express@11.1.28)': + dependencies: + '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + tslib: 2.8.1 + optionalDependencies: + '@nestjs/platform-express': 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28) + + '@noble/ciphers@1.3.0': {} + + '@noble/curves@1.9.7': + dependencies: + '@noble/hashes': 1.8.0 + + '@noble/hashes@1.4.0': {} + + '@noble/hashes@1.8.0': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@nx/devkit@23.0.2(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)))': + dependencies: + '@zkochan/js-yaml': 0.0.7 + ejs: 5.0.1 + enquirer: 2.3.6 + minimatch: 10.2.5 + nx: 23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)) + semver: 7.8.5 + tslib: 2.8.1 + yaml: 2.9.0 + yargs-parser: 21.1.1 + + '@nx/docker@23.0.2(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)))': + dependencies: + '@nx/devkit': 23.0.2(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))) + enquirer: 2.3.6 + tslib: 2.8.1 + transitivePeerDependencies: + - nx + + '@nx/eslint-plugin@23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(@typescript-eslint/parser@8.63.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint-config-prettier@10.1.8(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)))(typescript@5.9.3)': + dependencies: + '@nx/devkit': 23.0.2(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))) + '@nx/js': 23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))) + '@phenomnomnominal/tsquery': 6.2.0(typescript@5.9.3) + '@typescript-eslint/type-utils': 8.63.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/utils': 8.63.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3) + chalk: 4.1.2 + confusing-browser-globals: 1.0.11 + globals: 17.7.0 + jsonc-eslint-parser: 2.4.2 + semver: 7.8.5 + tslib: 2.8.1 + optionalDependencies: + '@typescript-eslint/parser': 8.63.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3) + eslint-config-prettier: 10.1.8(eslint@9.39.5(jiti@2.7.0)) + transitivePeerDependencies: + - '@babel/traverse' + - '@swc-node/register' + - '@swc/cli' + - '@swc/core' + - eslint + - nx + - supports-color + - typescript + - verdaccio + + '@nx/eslint@23.0.2(@babel/traverse@7.29.7)(@nx/jest@23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)))(ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.3.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(typescript@5.9.3))(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3))(typescript@5.9.3))(@swc/core@1.15.43(@swc/helpers@0.5.23))(@zkochan/js-yaml@0.0.7)(eslint@9.39.5(jiti@2.7.0))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)))': + dependencies: + '@nx/devkit': 23.0.2(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))) + '@nx/js': 23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))) + eslint: 9.39.5(jiti@2.7.0) + semver: 7.8.5 + tslib: 2.8.1 + typescript: 5.9.3 + optionalDependencies: + '@nx/jest': 23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)))(ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.3.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(typescript@5.9.3))(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3))(typescript@5.9.3) + '@zkochan/js-yaml': 0.0.7 + transitivePeerDependencies: + - '@babel/traverse' + - '@swc-node/register' + - '@swc/cli' + - '@swc/core' + - nx + - supports-color + - verdaccio + + '@nx/jest@23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)))(ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.3.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(typescript@5.9.3))(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3))(typescript@5.9.3)': + dependencies: + '@jest/reporters': 30.4.1 + '@jest/test-result': 30.4.1 + '@nx/devkit': 23.0.2(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))) + '@nx/js': 23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))) + '@phenomnomnominal/tsquery': 6.2.0(typescript@5.9.3) + identity-obj-proxy: 3.0.0 + jest-config: 30.4.2(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)) + jest-resolve: 30.4.1 + jest-util: 30.3.0 + minimatch: 10.2.5 + picocolors: 1.1.1 + resolve.exports: 2.0.3 + semver: 7.8.5 + tslib: 2.8.1 + yargs-parser: 21.1.1 + optionalDependencies: + jest: 30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)) + ts-jest: 29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.3.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(typescript@5.9.3) + transitivePeerDependencies: + - '@babel/traverse' + - '@swc-node/register' + - '@swc/cli' + - '@swc/core' + - '@types/node' + - babel-plugin-macros + - esbuild-register + - node-notifier + - nx + - supports-color + - ts-node + - typescript + - verdaccio + + '@nx/js@23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)))': + dependencies: + '@babel/core': 7.29.7 + '@babel/plugin-proposal-decorators': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-runtime': 7.29.7(@babel/core@7.29.7) + '@babel/preset-env': 7.29.7(@babel/core@7.29.7) + '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7) + '@babel/runtime': 7.29.7 + '@nx/devkit': 23.0.2(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))) + '@nx/workspace': 23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)) + '@zkochan/js-yaml': 0.0.7 + babel-plugin-const-enum: 1.2.0(@babel/core@7.29.7) + babel-plugin-macros: 3.1.0 + babel-plugin-transform-typescript-metadata: 0.3.2(@babel/core@7.29.7)(@babel/traverse@7.29.7) + chalk: 4.1.2 + columnify: 1.6.0 + detect-port: 2.1.0 + ignore: 7.0.6 + js-tokens: 4.0.0 + jsonc-parser: 3.3.1 + npm-run-path: 4.0.1 + picocolors: 1.1.1 + picomatch: 4.0.4 + semver: 7.8.5 + source-map-support: 0.5.19 + tinyglobby: 0.2.17 + tslib: 2.8.1 + transitivePeerDependencies: + - '@babel/traverse' + - '@swc-node/register' + - '@swc/core' + - nx + - supports-color + + '@nx/nest@23.0.2(d5d1d2d47871c69071379b8e39d48666)': + dependencies: + '@nestjs/schematics': 11.1.0(chokidar@4.0.3)(prettier@3.9.5)(typescript@5.9.3) + '@nx/devkit': 23.0.2(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))) + '@nx/eslint': 23.0.2(@babel/traverse@7.29.7)(@nx/jest@23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)))(ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.3.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(typescript@5.9.3))(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3))(typescript@5.9.3))(@swc/core@1.15.43(@swc/helpers@0.5.23))(@zkochan/js-yaml@0.0.7)(eslint@9.39.5(jiti@2.7.0))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))) + '@nx/js': 23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))) + '@nx/node': 23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(@zkochan/js-yaml@0.0.7)(babel-plugin-macros@3.1.0)(eslint@9.39.5(jiti@2.7.0))(express@5.2.1)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)))(ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.3.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(typescript@5.9.3))(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3))(typescript@5.9.3) + semver: 7.8.5 + tslib: 2.8.1 + optionalDependencies: + '@nestjs/common': 11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.28(@nestjs/common@11.1.28(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.28)(reflect-metadata@0.2.2)(rxjs@7.8.2) + reflect-metadata: 0.2.2 + rxjs: 7.8.2 + transitivePeerDependencies: + - '@babel/traverse' + - '@nx/jest' + - '@swc-node/register' + - '@swc/cli' + - '@swc/core' + - '@types/node' + - '@zkochan/js-yaml' + - babel-plugin-macros + - chokidar + - esbuild-register + - eslint + - express + - fastify + - jest + - koa + - node-notifier + - nx + - prettier + - supports-color + - ts-jest + - ts-node + - typescript + - verdaccio + + '@nx/node@23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(@zkochan/js-yaml@0.0.7)(babel-plugin-macros@3.1.0)(eslint@9.39.5(jiti@2.7.0))(express@5.2.1)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)))(ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.3.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(typescript@5.9.3))(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3))(typescript@5.9.3)': + dependencies: + '@nx/devkit': 23.0.2(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))) + '@nx/docker': 23.0.2(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))) + '@nx/eslint': 23.0.2(@babel/traverse@7.29.7)(@nx/jest@23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)))(ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.3.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(typescript@5.9.3))(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3))(typescript@5.9.3))(@swc/core@1.15.43(@swc/helpers@0.5.23))(@zkochan/js-yaml@0.0.7)(eslint@9.39.5(jiti@2.7.0))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))) + '@nx/jest': 23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)))(ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.3.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(typescript@5.9.3))(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3))(typescript@5.9.3) + '@nx/js': 23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))) + kill-port: 1.6.1 + semver: 7.8.5 + tcp-port-used: 1.0.3 + tslib: 2.8.1 + optionalDependencies: + express: 5.2.1 + transitivePeerDependencies: + - '@babel/traverse' + - '@swc-node/register' + - '@swc/cli' + - '@swc/core' + - '@types/node' + - '@zkochan/js-yaml' + - babel-plugin-macros + - esbuild-register + - eslint + - jest + - node-notifier + - nx + - supports-color + - ts-jest + - ts-node + - typescript + - verdaccio + + '@nx/nx-darwin-arm64@23.0.2': + optional: true + + '@nx/nx-darwin-x64@23.0.2': + optional: true + + '@nx/nx-freebsd-x64@23.0.2': + optional: true + + '@nx/nx-linux-arm-gnueabihf@23.0.2': + optional: true + + '@nx/nx-linux-arm64-gnu@23.0.2': + optional: true + + '@nx/nx-linux-arm64-musl@23.0.2': + optional: true + + '@nx/nx-linux-x64-gnu@23.0.2': + optional: true + + '@nx/nx-linux-x64-musl@23.0.2': + optional: true + + '@nx/nx-win32-arm64-msvc@23.0.2': + optional: true + + '@nx/nx-win32-x64-msvc@23.0.2': + optional: true + + '@nx/web@23.0.2(f62ac14c8923e53a1e4e1648925cde7c)': + dependencies: + '@nx/devkit': 23.0.2(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))) + '@nx/js': 23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))) + detect-port: 2.1.0 + http-server: 14.1.1 + picocolors: 1.1.1 + tslib: 2.8.1 + optionalDependencies: + '@nx/eslint': 23.0.2(@babel/traverse@7.29.7)(@nx/jest@23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)))(ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.3.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(typescript@5.9.3))(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3))(typescript@5.9.3))(@swc/core@1.15.43(@swc/helpers@0.5.23))(@zkochan/js-yaml@0.0.7)(eslint@9.39.5(jiti@2.7.0))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))) + '@nx/jest': 23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)))(ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.3.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(typescript@5.9.3))(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3))(typescript@5.9.3) + '@nx/webpack': 23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)))(typescript@5.9.3)(webpack-cli@7.2.1)(webpack-dev-server@5.2.6)(webpack@5.108.4) + transitivePeerDependencies: + - '@babel/traverse' + - '@swc-node/register' + - '@swc/cli' + - '@swc/core' + - debug + - nx + - supports-color + - verdaccio + + '@nx/webpack@23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)))(typescript@5.9.3)(webpack-cli@7.2.1)(webpack-dev-server@5.2.6)(webpack@5.108.4)': + dependencies: + '@babel/core': 7.29.7 + '@nx/devkit': 23.0.2(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))) + '@nx/js': 23.0.2(@babel/traverse@7.29.7)(@swc/core@1.15.43(@swc/helpers@0.5.23))(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))) + '@phenomnomnominal/tsquery': 6.2.0(typescript@5.9.3) + ajv: 8.20.0 + autoprefixer: 10.5.2(postcss@8.5.18) + babel-loader: 9.2.1(@babel/core@7.29.7)(webpack@5.108.4) + browserslist: 4.28.6 + copy-webpack-plugin: 14.0.0(webpack@5.108.4) + css-loader: 6.11.0(webpack@5.108.4) + css-minimizer-webpack-plugin: 8.0.0(webpack@5.108.4) + fork-ts-checker-webpack-plugin: 9.1.0(typescript@5.9.3)(webpack@5.108.4) + less: 4.5.1 + less-loader: 12.3.3(less@4.5.1)(webpack@5.108.4) + license-webpack-plugin: 4.0.2(webpack@5.108.4) + loader-utils: 2.0.4 + mini-css-extract-plugin: 2.4.7(webpack@5.108.4) + parse5: 4.0.0 + picocolors: 1.1.1 + postcss: 8.5.18 + postcss-import: 14.1.0(postcss@8.5.18) + postcss-loader: 8.2.1(postcss@8.5.18)(typescript@5.9.3)(webpack@5.108.4) + rxjs: 7.8.2 + sass: 1.101.0 + sass-embedded: 1.100.0 + sass-loader: 16.0.8(sass-embedded@1.100.0)(sass@1.101.0)(webpack@5.108.4) + source-map-loader: 5.0.0(webpack@5.108.4) + style-loader: 3.3.4(webpack@5.108.4) + terser-webpack-plugin: 5.6.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack@5.108.4) + ts-loader: 9.6.2(loader-utils@2.0.4)(typescript@5.9.3)(webpack@5.108.4) + tsconfig-paths-webpack-plugin: 4.2.0 + tslib: 2.8.1 + webpack-node-externals: 3.0.0 + webpack-subresource-integrity: 5.1.0(webpack@5.108.4) + optionalDependencies: + webpack: 5.108.4(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack-cli@7.2.1) + webpack-cli: 7.2.1(js-yaml@4.3.0)(json5@2.2.3)(webpack-dev-server@5.2.6)(webpack@5.108.4) + webpack-dev-server: 5.2.6(tslib@2.8.1)(webpack-cli@7.2.1)(webpack@5.108.4) + transitivePeerDependencies: + - '@babel/traverse' + - '@minify-html/node' + - '@parcel/css' + - '@rspack/core' + - '@swc-node/register' + - '@swc/cli' + - '@swc/core' + - '@swc/css' + - '@swc/html' + - clean-css + - cssnano + - csso + - esbuild + - html-minifier-terser + - html-webpack-plugin + - lightningcss + - node-sass + - nx + - supports-color + - typescript + - uglify-js + - verdaccio + + '@nx/workspace@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))': + dependencies: + '@nx/devkit': 23.0.2(nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23))) + '@zkochan/js-yaml': 0.0.7 + chalk: 4.1.2 + enquirer: 2.3.6 + nx: 23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)) + picomatch: 4.0.4 + semver: 7.8.5 + tslib: 2.8.1 + yargs-parser: 21.1.1 + transitivePeerDependencies: + - '@swc-node/register' + - '@swc/core' + + '@paralleldrive/cuid2@2.3.1': + dependencies: + '@noble/hashes': 1.8.0 + + '@parcel/watcher-android-arm64@2.5.6': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.6': + optional: true + + '@parcel/watcher-darwin-x64@2.5.6': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.6': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.6': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.6': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.6': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.6': + optional: true + + '@parcel/watcher-win32-arm64@2.5.6': + optional: true + + '@parcel/watcher-win32-ia32@2.5.6': + optional: true + + '@parcel/watcher-win32-x64@2.5.6': + optional: true + + '@parcel/watcher@2.5.6': + dependencies: + detect-libc: 2.1.2 + is-glob: 4.0.3 + node-addon-api: 7.1.1 + picomatch: 4.0.5 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.6 + '@parcel/watcher-darwin-arm64': 2.5.6 + '@parcel/watcher-darwin-x64': 2.5.6 + '@parcel/watcher-freebsd-x64': 2.5.6 + '@parcel/watcher-linux-arm-glibc': 2.5.6 + '@parcel/watcher-linux-arm-musl': 2.5.6 + '@parcel/watcher-linux-arm64-glibc': 2.5.6 + '@parcel/watcher-linux-arm64-musl': 2.5.6 + '@parcel/watcher-linux-x64-glibc': 2.5.6 + '@parcel/watcher-linux-x64-musl': 2.5.6 + '@parcel/watcher-win32-arm64': 2.5.6 + '@parcel/watcher-win32-ia32': 2.5.6 + '@parcel/watcher-win32-x64': 2.5.6 + optional: true + + '@peculiar/asn1-cms@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + '@peculiar/asn1-x509-attr': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-csr@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-ecc@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-pfx@2.8.0': + dependencies: + '@peculiar/asn1-cms': 2.8.0 + '@peculiar/asn1-pkcs8': 2.8.0 + '@peculiar/asn1-rsa': 2.8.0 + '@peculiar/asn1-schema': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-pkcs8@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-pkcs9@2.8.0': + dependencies: + '@peculiar/asn1-cms': 2.8.0 + '@peculiar/asn1-pfx': 2.8.0 + '@peculiar/asn1-pkcs8': 2.8.0 + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + '@peculiar/asn1-x509-attr': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-rsa@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-schema@2.8.0': + dependencies: + '@peculiar/utils': 2.0.3 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-x509-attr@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-x509@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/utils': 2.0.3 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/utils@2.0.3': + dependencies: + tslib: 2.8.1 + + '@peculiar/x509@1.14.3': + dependencies: + '@peculiar/asn1-cms': 2.8.0 + '@peculiar/asn1-csr': 2.8.0 + '@peculiar/asn1-ecc': 2.8.0 + '@peculiar/asn1-pkcs9': 2.8.0 + '@peculiar/asn1-rsa': 2.8.0 + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + pvtsutils: 1.3.6 + reflect-metadata: 0.2.2 + tslib: 2.8.1 + tsyringe: 4.10.0 + + '@phenomnomnominal/tsquery@6.2.0(typescript@5.9.3)': + dependencies: + '@types/esquery': 1.5.4 + esquery: 1.7.0 + typescript: 5.9.3 + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@pkgr/core@0.3.6': {} + + '@sinclair/typebox@0.34.52': {} + + '@sinonjs/commons@3.0.1': + dependencies: + type-detect: 4.0.8 + + '@sinonjs/fake-timers@15.4.0': + dependencies: + '@sinonjs/commons': 3.0.1 + + '@supabase/auth-js@2.110.5': + dependencies: + tslib: 2.8.1 + + '@supabase/cli-darwin-arm64@2.109.1': + optional: true + + '@supabase/cli-darwin-x64@2.109.1': + optional: true + + '@supabase/cli-linux-arm64-musl@2.109.1': + optional: true + + '@supabase/cli-linux-arm64@2.109.1': + optional: true + + '@supabase/cli-linux-x64-musl@2.109.1': + optional: true + + '@supabase/cli-linux-x64@2.109.1': + optional: true + + '@supabase/cli-windows-arm64@2.109.1': + optional: true + + '@supabase/cli-windows-x64@2.109.1': + optional: true + + '@supabase/functions-js@2.110.5': + dependencies: + tslib: 2.8.1 + + '@supabase/phoenix@0.4.4': {} + + '@supabase/postgrest-js@2.110.5': + dependencies: + tslib: 2.8.1 + + '@supabase/realtime-js@2.110.5': + dependencies: + '@supabase/phoenix': 0.4.4 + tslib: 2.8.1 + + '@supabase/storage-js@2.110.5': + dependencies: + iceberg-js: 0.8.1 + tslib: 2.8.1 + + '@supabase/supabase-js@2.110.5': + dependencies: + '@supabase/auth-js': 2.110.5 + '@supabase/functions-js': 2.110.5 + '@supabase/postgrest-js': 2.110.5 + '@supabase/realtime-js': 2.110.5 + '@supabase/storage-js': 2.110.5 + + '@swc/core-darwin-arm64@1.15.43': + optional: true + + '@swc/core-darwin-x64@1.15.43': + optional: true + + '@swc/core-linux-arm-gnueabihf@1.15.43': + optional: true + + '@swc/core-linux-arm64-gnu@1.15.43': + optional: true + + '@swc/core-linux-arm64-musl@1.15.43': + optional: true + + '@swc/core-linux-ppc64-gnu@1.15.43': + optional: true + + '@swc/core-linux-s390x-gnu@1.15.43': + optional: true + + '@swc/core-linux-x64-gnu@1.15.43': + optional: true + + '@swc/core-linux-x64-musl@1.15.43': + optional: true + + '@swc/core-win32-arm64-msvc@1.15.43': + optional: true + + '@swc/core-win32-ia32-msvc@1.15.43': + optional: true + + '@swc/core-win32-x64-msvc@1.15.43': + optional: true + + '@swc/core@1.15.43(@swc/helpers@0.5.23)': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.27 + optionalDependencies: + '@swc/core-darwin-arm64': 1.15.43 + '@swc/core-darwin-x64': 1.15.43 + '@swc/core-linux-arm-gnueabihf': 1.15.43 + '@swc/core-linux-arm64-gnu': 1.15.43 + '@swc/core-linux-arm64-musl': 1.15.43 + '@swc/core-linux-ppc64-gnu': 1.15.43 + '@swc/core-linux-s390x-gnu': 1.15.43 + '@swc/core-linux-x64-gnu': 1.15.43 + '@swc/core-linux-x64-musl': 1.15.43 + '@swc/core-win32-arm64-msvc': 1.15.43 + '@swc/core-win32-ia32-msvc': 1.15.43 + '@swc/core-win32-x64-msvc': 1.15.43 + '@swc/helpers': 0.5.23 + + '@swc/counter@0.1.3': {} + + '@swc/helpers@0.5.23': + dependencies: + tslib: 2.8.1 + + '@swc/jest@0.2.39(@swc/core@1.15.43(@swc/helpers@0.5.23))': + dependencies: + '@jest/create-cache-key-function': 30.4.1 + '@swc/core': 1.15.43(@swc/helpers@0.5.23) + '@swc/counter': 0.1.3 + jsonc-parser: 3.3.1 + + '@swc/types@0.1.27': + dependencies: + '@swc/counter': 0.1.3 + + '@tokenizer/inflate@0.4.1': + dependencies: + debug: 4.4.3(supports-color@7.2.0) + token-types: 6.1.2 + transitivePeerDependencies: + - supports-color + + '@tokenizer/token@0.3.0': {} + + '@ts-morph/common@0.19.0': + dependencies: + fast-glob: 3.3.3 + minimatch: 7.4.9 + mkdirp: 2.1.6 + path-browserify: 1.0.1 + + '@tsconfig/node10@1.0.12': {} + + '@tsconfig/node12@1.0.11': {} + + '@tsconfig/node14@1.0.3': {} + + '@tsconfig/node16@1.0.4': {} + + '@tybys/wasm-util@0.10.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@tybys/wasm-util@0.9.0': + dependencies: + tslib: 2.8.1 + + '@types/accepts@1.3.7': + dependencies: + '@types/node': 22.20.1 + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.7 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.7 + + '@types/body-parser@1.19.6': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 22.20.1 + + '@types/bonjour@3.5.13': + dependencies: + '@types/node': 22.20.1 + + '@types/connect-history-api-fallback@1.5.4': + dependencies: + '@types/express-serve-static-core': 4.19.9 + '@types/node': 22.20.1 + + '@types/connect@3.4.38': + dependencies: + '@types/node': 22.20.1 + + '@types/content-disposition@0.5.9': {} + + '@types/cookiejar@2.1.5': {} + + '@types/cookies@0.9.2': + dependencies: + '@types/connect': 3.4.38 + '@types/express': 4.17.25 + '@types/keygrip': 1.0.6 + '@types/node': 22.20.1 + + '@types/esquery@1.5.4': + dependencies: + '@types/estree': 1.0.9 + + '@types/estree@1.0.9': {} + + '@types/express-serve-static-core@4.19.9': + dependencies: + '@types/node': 22.20.1 + '@types/qs': 6.15.1 + '@types/range-parser': 1.2.7 + '@types/send': 1.2.1 + + '@types/express@4.17.25': + dependencies: + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 4.19.9 + '@types/qs': 6.15.1 + '@types/serve-static': 1.15.10 + + '@types/http-assert@1.5.6': {} + + '@types/http-errors@2.0.5': {} + + '@types/http-proxy@1.17.17': + dependencies: + '@types/node': 22.20.1 + + '@types/istanbul-lib-coverage@2.0.6': {} + + '@types/istanbul-lib-report@3.0.3': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + + '@types/istanbul-reports@3.0.4': + dependencies: + '@types/istanbul-lib-report': 3.0.3 + + '@types/jest@30.0.0': + dependencies: + expect: 30.4.1 + pretty-format: 30.4.1 + + '@types/json-schema@7.0.15': {} + + '@types/keygrip@1.0.6': {} + + '@types/koa-compose@3.2.9': + dependencies: + '@types/koa': 3.0.3 + + '@types/koa@3.0.3': + dependencies: + '@types/accepts': 1.3.7 + '@types/content-disposition': 0.5.9 + '@types/cookies': 0.9.2 + '@types/http-assert': 1.5.6 + '@types/http-errors': 2.0.5 + '@types/keygrip': 1.0.6 + '@types/koa-compose': 3.2.9 + '@types/node': 22.20.1 + + '@types/luxon@3.7.2': {} + + '@types/methods@1.1.4': {} + + '@types/mime@1.3.5': {} + + '@types/node@22.20.1': + dependencies: + undici-types: 6.21.0 + + '@types/parse-json@4.0.2': {} + + '@types/passport-http-bearer@1.0.42': + dependencies: + '@types/express': 4.17.25 + '@types/koa': 3.0.3 + '@types/passport': 1.0.17 + + '@types/passport@1.0.17': + dependencies: + '@types/express': 4.17.25 + + '@types/qs@6.15.1': {} + + '@types/ramda@0.31.1': + dependencies: + types-ramda: 0.31.0 + + '@types/range-parser@1.2.7': {} + + '@types/retry@0.12.2': {} + + '@types/send@0.17.6': + dependencies: + '@types/mime': 1.3.5 + '@types/node': 22.20.1 + + '@types/send@1.2.1': + dependencies: + '@types/node': 22.20.1 + + '@types/serve-index@1.9.4': + dependencies: + '@types/express': 4.17.25 + + '@types/serve-static@1.15.10': + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 22.20.1 + '@types/send': 0.17.6 + + '@types/sockjs@0.3.36': + dependencies: + '@types/node': 22.20.1 + + '@types/stack-utils@2.0.3': {} + + '@types/superagent@8.1.11': + dependencies: + '@types/cookiejar': 2.1.5 + '@types/methods': 1.1.4 + '@types/node': 22.20.1 + form-data: 4.0.6 + + '@types/supertest@7.2.1': + dependencies: + '@types/methods': 1.1.4 + '@types/superagent': 8.1.11 + + '@types/validator@13.15.10': {} + + '@types/ws@8.18.1': + dependencies: + '@types/node': 22.20.1 + + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@17.0.35': + dependencies: + '@types/yargs-parser': 21.0.3 + + '@typescript-eslint/eslint-plugin@8.63.0(@typescript-eslint/parser@8.63.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.63.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.63.0 + '@typescript-eslint/type-utils': 8.63.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/utils': 8.63.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.63.0 + eslint: 9.39.5(jiti@2.7.0) + ignore: 7.0.6 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.63.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.63.0 + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/typescript-estree': 8.63.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.63.0 + debug: 4.4.3(supports-color@7.2.0) + eslint: 9.39.5(jiti@2.7.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.63.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.63.0(typescript@5.9.3) + '@typescript-eslint/types': 8.63.0 + debug: 4.4.3(supports-color@7.2.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.63.0': + dependencies: + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/visitor-keys': 8.63.0 + + '@typescript-eslint/tsconfig-utils@8.63.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.63.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/typescript-estree': 8.63.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.63.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3) + debug: 4.4.3(supports-color@7.2.0) + eslint: 9.39.5(jiti@2.7.0) + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.63.0': {} + + '@typescript-eslint/typescript-estree@8.63.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.63.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.63.0(typescript@5.9.3) + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/visitor-keys': 8.63.0 + debug: 4.4.3(supports-color@7.2.0) + minimatch: 10.2.5 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.63.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5(jiti@2.7.0)) + '@typescript-eslint/scope-manager': 8.63.0 + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/typescript-estree': 8.63.0(typescript@5.9.3) + eslint: 9.39.5(jiti@2.7.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.63.0': + dependencies: + '@typescript-eslint/types': 8.63.0 + eslint-visitor-keys: 5.0.1 + + '@ungap/structured-clone@1.3.3': {} + + '@unrs/resolver-binding-android-arm-eabi@1.12.2': + optional: true + + '@unrs/resolver-binding-android-arm64@1.12.2': + optional: true + + '@unrs/resolver-binding-darwin-arm64@1.12.2': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.12.2': + optional: true + + '@unrs/resolver-binding-freebsd-x64@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-loong64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-loong64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-s390x-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-x64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-openharmony-arm64@1.12.2': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.12.2': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': + optional: true + + '@unrs/resolver-binding-win32-ia32-msvc@1.12.2': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.12.2': + optional: true + + '@webassemblyjs/ast@1.14.1': + dependencies: + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + + '@webassemblyjs/floating-point-hex-parser@1.13.2': {} + + '@webassemblyjs/helper-api-error@1.13.2': {} + + '@webassemblyjs/helper-buffer@1.14.1': {} + + '@webassemblyjs/helper-numbers@1.13.2': + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} + + '@webassemblyjs/helper-wasm-section@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/wasm-gen': 1.14.1 + + '@webassemblyjs/ieee754@1.13.2': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/leb128@1.13.2': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/utf8@1.13.2': {} + + '@webassemblyjs/wasm-edit@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/helper-wasm-section': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-opt': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + '@webassemblyjs/wast-printer': 1.14.1 + + '@webassemblyjs/wasm-gen@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wasm-opt@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + + '@webassemblyjs/wasm-parser@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-api-error': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wast-printer@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@xtuc/long': 4.2.2 + + '@xtuc/ieee754@1.2.0': {} + + '@xtuc/long@4.2.2': {} + + '@yarnpkg/lockfile@1.1.0': {} + + '@zkochan/js-yaml@0.0.7': + dependencies: + argparse: 2.0.1 + + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + accepts@2.0.0: + dependencies: + mime-types: 3.0.2 + negotiator: 1.0.0 + + acorn-import-phases@1.0.4(acorn@8.17.0): + dependencies: + acorn: 8.17.0 + + acorn-jsx@5.3.2(acorn@8.17.0): + dependencies: + acorn: 8.17.0 + + acorn-walk@8.3.5: + dependencies: + acorn: 8.17.0 + + acorn@8.17.0: {} + + address@2.0.3: {} + + agent-base@6.0.2(supports-color@7.2.0): + dependencies: + debug: 4.4.3(supports-color@7.2.0) + transitivePeerDependencies: + - supports-color + + ajv-formats@2.1.1(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + + ajv-formats@3.0.1(ajv@8.18.0): + optionalDependencies: + ajv: 8.18.0 + + ajv-keywords@3.5.2(ajv@6.15.0): + dependencies: + ajv: 6.15.0 + + ajv-keywords@5.1.0(ajv@8.20.0): + dependencies: + ajv: 8.20.0 + fast-deep-equal: 3.1.3 + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.18.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-colors@4.1.3: {} + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-escapes@7.3.0: + dependencies: + environment: 1.1.0 + + ansi-html-community@0.0.8: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + ansi-styles@6.2.3: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.2 + + append-field@1.0.0: {} + + arg@4.1.3: {} + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + array-flatten@1.1.1: {} + + array-timsort@1.0.3: {} + + asap@2.0.6: {} + + asn1js@3.0.10: + dependencies: + pvtsutils: 1.3.6 + pvutils: 1.1.5 + tslib: 2.8.1 + + async@3.2.6: {} + + asynckit@0.4.0: {} + + autoprefixer@10.5.2(postcss@8.5.18): + dependencies: + browserslist: 4.28.6 + caniuse-lite: 1.0.30001805 + fraction.js: 5.3.4 + picocolors: 1.1.1 + postcss: 8.5.18 + postcss-value-parser: 4.2.0 + + axios@1.16.1(debug@4.4.3(supports-color@7.2.0))(supports-color@7.2.0): + dependencies: + follow-redirects: 1.16.0(debug@4.4.3(supports-color@7.2.0)) + form-data: 4.0.6 + https-proxy-agent: 5.0.1(supports-color@7.2.0) + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + - supports-color + + axios@1.18.1: + dependencies: + follow-redirects: 1.16.0(debug@4.4.3(supports-color@7.2.0)) + form-data: 4.0.6 + https-proxy-agent: 5.0.1(supports-color@7.2.0) + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + - supports-color + + babel-jest@30.3.0(@babel/core@7.29.7): + dependencies: + '@babel/core': 7.29.7 + '@jest/transform': 30.3.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 7.0.1 + babel-preset-jest: 30.3.0(@babel/core@7.29.7) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + + babel-jest@30.4.1(@babel/core@7.29.7): + dependencies: + '@babel/core': 7.29.7 + '@jest/transform': 30.4.1 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 7.0.1 + babel-preset-jest: 30.4.0(@babel/core@7.29.7) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + + babel-loader@9.2.1(@babel/core@7.29.7)(webpack@5.108.4): + dependencies: + '@babel/core': 7.29.7 + find-cache-dir: 4.0.0 + schema-utils: 4.3.3 + webpack: 5.108.4(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack-cli@7.2.1) + + babel-plugin-const-enum@1.2.0(@babel/core@7.29.7): + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + babel-plugin-istanbul@7.0.1: + dependencies: + '@babel/helper-plugin-utils': 7.29.7 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.6 + istanbul-lib-instrument: 6.0.3 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-jest-hoist@30.3.0: + dependencies: + '@types/babel__core': 7.20.5 + + babel-plugin-jest-hoist@30.4.0: + dependencies: + '@types/babel__core': 7.20.5 + + babel-plugin-macros@3.1.0: + dependencies: + '@babel/runtime': 7.29.7 + cosmiconfig: 7.1.0 + resolve: 1.22.12 + + babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.7): + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/core': 7.29.7 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.29.7): + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7) + core-js-compat: 3.49.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.14.2(@babel/core@7.29.7): + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7) + core-js-compat: 3.49.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.29.7): + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + + babel-plugin-transform-typescript-metadata@0.3.2(@babel/core@7.29.7)(@babel/traverse@7.29.7): + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + optionalDependencies: + '@babel/traverse': 7.29.7 + + babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.7): + dependencies: + '@babel/core': 7.29.7 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.7) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.7) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.7) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.7) + '@babel/plugin-syntax-import-attributes': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.7) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.7) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.7) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.7) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.7) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.7) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.7) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.7) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.7) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.7) + + babel-preset-jest@30.3.0(@babel/core@7.29.7): + dependencies: + '@babel/core': 7.29.7 + babel-plugin-jest-hoist: 30.3.0 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.7) + + babel-preset-jest@30.4.0(@babel/core@7.29.7): + dependencies: + '@babel/core': 7.29.7 + babel-plugin-jest-hoist: 30.4.0 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.7) + + balanced-match@1.0.2: {} + + balanced-match@4.0.3: {} + + balanced-match@4.0.4: {} + + base64-js@1.5.1: {} + + baseline-browser-mapping@2.10.43: {} + + basic-auth@2.0.1: + dependencies: + safe-buffer: 5.1.2 + + batch@0.6.1: {} + + better-supabase-types@2.7.6: + dependencies: + chalk: 4.1.2 + pluralize: 8.0.0 + prettier: 3.9.5 + ts-morph: 18.0.0 + yargs: 17.7.3 + zod: 3.25.76 + + big.js@5.2.2: {} + + binary-extensions@2.3.0: {} + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + body-parser@1.20.6: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.15.3 + raw-body: 2.5.3 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + body-parser@2.3.0: + dependencies: + bytes: 3.1.2 + content-type: 2.0.0 + debug: 4.4.3(supports-color@7.2.0) + http-errors: 2.0.1 + iconv-lite: 0.7.3 + on-finished: 2.4.1 + qs: 6.15.3 + raw-body: 3.0.2 + type-is: 2.1.0 + transitivePeerDependencies: + - supports-color + + bonjour-service@1.4.3: + dependencies: + fast-deep-equal: 3.1.3 + multicast-dns: 7.2.5 + + boolbase@1.0.0: {} + + brace-expansion@1.1.16: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.1.2: + dependencies: + balanced-match: 1.0.2 + + brace-expansion@5.0.6: + dependencies: + balanced-match: 4.0.3 + + brace-expansion@5.0.7: + dependencies: + balanced-match: 4.0.4 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.6: + dependencies: + baseline-browser-mapping: 2.10.43 + caniuse-lite: 1.0.30001805 + electron-to-chromium: 1.5.389 + node-releases: 2.0.51 + update-browserslist-db: 1.2.3(browserslist@4.28.6) + + bs-logger@0.2.6: + dependencies: + fast-json-stable-stringify: 2.1.0 + + bser@2.1.1: + dependencies: + node-int64: 0.4.0 + + buffer-from@1.1.2: {} + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + bundle-name@4.1.0: + dependencies: + run-applescript: 7.1.0 + + busboy@1.6.0: + dependencies: + streamsearch: 1.1.0 + + bytes@3.1.2: {} + + bytestreamjs@2.0.1: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + camelcase@5.3.1: {} + + camelcase@6.3.0: {} + + caniuse-api@3.0.0: + dependencies: + browserslist: 4.28.6 + caniuse-lite: 1.0.30001805 + lodash.memoize: 4.1.2 + lodash.uniq: 4.5.0 + + caniuse-lite@1.0.30001805: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + char-regex@1.0.2: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + + chrome-trace-event@1.0.4: {} + + ci-info@4.4.0: {} + + cjs-module-lexer@2.2.0: {} + + class-transformer@0.5.1: {} + + class-validator@0.15.1: + dependencies: + '@types/validator': 13.15.10 + libphonenumber-js: 1.13.8 + validator: 13.15.35 + + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-spinners@2.6.1: {} + + cli-truncate@5.2.0: + dependencies: + slice-ansi: 8.0.0 + string-width: 8.2.2 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clone-deep@4.0.1: + dependencies: + is-plain-object: 2.0.4 + kind-of: 6.0.3 + shallow-clone: 3.0.1 + + clone@1.0.4: {} + + co@4.6.0: {} + + code-block-writer@12.0.0: {} + + collect-v8-coverage@1.0.3: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + colorette@2.0.20: {} + + colorjs.io@0.5.2: {} + + columnify@1.6.0: + dependencies: + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@11.1.0: {} + + commander@14.0.3: {} + + commander@2.20.3: {} + + comment-json@5.0.0: + dependencies: + array-timsort: 1.0.3 + esprima: 4.0.1 + + common-path-prefix@3.0.0: {} + + component-emitter@1.3.1: {} + + compressible@2.0.18: + dependencies: + mime-db: 1.54.0 + + compression@1.8.1: + dependencies: + bytes: 3.1.2 + compressible: 2.0.18 + debug: 2.6.9 + negotiator: 0.6.4 + on-headers: 1.1.0 + safe-buffer: 5.2.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + concat-map@0.0.1: {} + + concat-stream@2.0.0: + dependencies: + buffer-from: 1.1.2 + inherits: 2.0.4 + readable-stream: 3.6.2 + typedarray: 0.0.6 + + confusing-browser-globals@1.0.11: {} + + connect-history-api-fallback@2.0.0: {} + + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + + content-disposition@1.1.0: {} + + content-type@1.0.5: {} + + content-type@2.0.0: {} + + convert-source-map@2.0.0: {} + + cookie-signature@1.0.7: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + + cookiejar@2.1.4: {} + + copy-anything@2.0.6: + dependencies: + is-what: 3.14.1 + + copy-webpack-plugin@14.0.0(webpack@5.108.4): + dependencies: + glob-parent: 6.0.2 + normalize-path: 3.0.0 + schema-utils: 4.3.3 + serialize-javascript: 7.0.7 + tinyglobby: 0.2.17 + webpack: 5.108.4(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack-cli@7.2.1) + + core-js-compat@3.49.0: + dependencies: + browserslist: 4.28.6 + + core-util-is@1.0.3: {} + + cors@2.8.6: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + corser@2.0.1: {} + + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.3 + + cosmiconfig@8.3.6(typescript@5.9.3): + dependencies: + import-fresh: 3.3.1 + js-yaml: 4.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + optionalDependencies: + typescript: 5.9.3 + + cosmiconfig@9.0.2(typescript@5.9.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.3.0 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.9.3 + + create-require@1.1.1: {} + + cron@4.4.0: + dependencies: + '@types/luxon': 3.7.2 + luxon: 3.7.2 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-declaration-sorter@7.4.0(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + + css-loader@6.11.0(webpack@5.108.4): + dependencies: + icss-utils: 5.1.0(postcss@8.5.18) + postcss: 8.5.18 + postcss-modules-extract-imports: 3.1.0(postcss@8.5.18) + postcss-modules-local-by-default: 4.2.0(postcss@8.5.18) + postcss-modules-scope: 3.2.1(postcss@8.5.18) + postcss-modules-values: 4.0.0(postcss@8.5.18) + postcss-value-parser: 4.2.0 + semver: 7.8.5 + optionalDependencies: + webpack: 5.108.4(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack-cli@7.2.1) + + css-minimizer-webpack-plugin@8.0.0(webpack@5.108.4): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + cssnano: 7.1.9(postcss@8.5.18) + jest-worker: 30.4.1 + postcss: 8.5.18 + schema-utils: 4.3.3 + serialize-javascript: 7.0.7 + webpack: 5.108.4(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack-cli@7.2.1) + + css-select@5.2.2: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-tree@2.2.1: + dependencies: + mdn-data: 2.0.28 + source-map-js: 1.2.1 + + css-tree@3.2.1: + dependencies: + mdn-data: 2.27.1 + source-map-js: 1.2.1 + + css-what@6.2.2: {} + + cssesc@3.0.0: {} + + cssnano-preset-default@7.0.17(postcss@8.5.18): + dependencies: + browserslist: 4.28.6 + css-declaration-sorter: 7.4.0(postcss@8.5.18) + cssnano-utils: 5.0.3(postcss@8.5.18) + postcss: 8.5.18 + postcss-calc: 10.1.1(postcss@8.5.18) + postcss-colormin: 7.0.10(postcss@8.5.18) + postcss-convert-values: 7.0.12(postcss@8.5.18) + postcss-discard-comments: 7.0.8(postcss@8.5.18) + postcss-discard-duplicates: 7.0.4(postcss@8.5.18) + postcss-discard-empty: 7.0.3(postcss@8.5.18) + postcss-discard-overridden: 7.0.3(postcss@8.5.18) + postcss-merge-longhand: 7.0.7(postcss@8.5.18) + postcss-merge-rules: 7.0.11(postcss@8.5.18) + postcss-minify-font-values: 7.0.3(postcss@8.5.18) + postcss-minify-gradients: 7.0.5(postcss@8.5.18) + postcss-minify-params: 7.0.9(postcss@8.5.18) + postcss-minify-selectors: 7.1.2(postcss@8.5.18) + postcss-normalize-charset: 7.0.3(postcss@8.5.18) + postcss-normalize-display-values: 7.0.3(postcss@8.5.18) + postcss-normalize-positions: 7.0.4(postcss@8.5.18) + postcss-normalize-repeat-style: 7.0.4(postcss@8.5.18) + postcss-normalize-string: 7.0.3(postcss@8.5.18) + postcss-normalize-timing-functions: 7.0.3(postcss@8.5.18) + postcss-normalize-unicode: 7.0.9(postcss@8.5.18) + postcss-normalize-url: 7.0.3(postcss@8.5.18) + postcss-normalize-whitespace: 7.0.3(postcss@8.5.18) + postcss-ordered-values: 7.0.4(postcss@8.5.18) + postcss-reduce-initial: 7.0.9(postcss@8.5.18) + postcss-reduce-transforms: 7.0.3(postcss@8.5.18) + postcss-svgo: 7.1.3(postcss@8.5.18) + postcss-unique-selectors: 7.0.7(postcss@8.5.18) + + cssnano-utils@5.0.3(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + + cssnano@7.1.9(postcss@8.5.18): + dependencies: + cssnano-preset-default: 7.0.17(postcss@8.5.18) + lilconfig: 3.1.3 + postcss: 8.5.18 + + csso@5.0.5: + dependencies: + css-tree: 2.2.1 + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + debug@4.3.1: + dependencies: + ms: 2.1.2 + + debug@4.4.3(supports-color@7.2.0): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 7.2.0 + + dedent@1.7.2(babel-plugin-macros@3.1.0): + optionalDependencies: + babel-plugin-macros: 3.1.0 + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + default-browser-id@5.0.1: {} + + default-browser@5.5.0: + dependencies: + bundle-name: 4.1.0 + default-browser-id: 5.0.1 + + defaults@1.0.4: + dependencies: + clone: 1.0.4 + + define-lazy-prop@2.0.0: {} + + define-lazy-prop@3.0.0: {} + + delayed-stream@1.0.0: {} + + depd@1.1.2: {} + + depd@2.0.0: {} + + destroy@1.2.0: {} + + detect-libc@2.1.2: + optional: true + + detect-newline@3.1.0: {} + + detect-node@2.1.0: {} + + detect-port@2.1.0: + dependencies: + address: 2.0.3 + + dezalgo@1.0.4: + dependencies: + asap: 2.0.6 + wrappy: 1.0.2 + + diff@4.0.4: {} + + dns-packet@5.6.1: + dependencies: + '@leichtgewicht/ip-codec': 2.0.5 + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dotenv-expand@12.0.3: + dependencies: + dotenv: 16.4.7 + + dotenv@16.4.7: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + eastasianwidth@0.2.0: {} + + eciesjs@0.5.0: + dependencies: + '@ecies/ciphers': 0.2.6(@noble/ciphers@1.3.0) + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + + ee-first@1.1.1: {} + + ejs@5.0.1: {} + + electron-to-chromium@1.5.389: {} + + emittery@0.13.1: {} + + emoji-regex@10.6.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + emojis-list@3.0.0: {} + + encodeurl@2.0.0: {} + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + enhanced-resolve@5.24.2: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + enquirer@2.3.6: + dependencies: + ansi-colors: 4.1.3 + + entities@4.5.0: {} + + env-paths@2.2.1: {} + + envinfo@7.21.0: {} + + environment@1.1.0: {} + + errno@0.1.8: + dependencies: + prr: 1.0.1 + optional: true + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@2.3.1: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + escalade@3.2.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@2.0.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-prettier@10.1.8(eslint@9.39.5(jiti@2.7.0)): + dependencies: + eslint: 9.39.5(jiti@2.7.0) + + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@9.39.5(jiti@2.7.0): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5(jiti@2.7.0)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.6 + '@eslint/js': 9.39.5 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@7.2.0) + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.7.0 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.17.0 + acorn-jsx: 5.3.2(acorn@8.17.0) + eslint-visitor-keys: 4.2.1 + + espree@9.6.1: + dependencies: + acorn: 8.17.0 + acorn-jsx: 5.3.2(acorn@8.17.0) + eslint-visitor-keys: 3.4.3 + + esprima@4.0.1: {} + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@4.3.0: {} + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + etag@1.8.1: {} + + eventemitter3@4.0.7: {} + + eventemitter3@5.0.4: {} + + events@3.3.0: {} + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + exit-x@0.2.2: {} + + expect@30.3.0: + dependencies: + '@jest/expect-utils': 30.3.0 + '@jest/get-type': 30.1.0 + jest-matcher-utils: 30.3.0 + jest-message-util: 30.3.0 + jest-mock: 30.3.0 + jest-util: 30.3.0 + + expect@30.4.1: + dependencies: + '@jest/expect-utils': 30.4.1 + '@jest/get-type': 30.1.0 + jest-matcher-utils: 30.4.1 + jest-message-util: 30.4.1 + jest-mock: 30.4.1 + jest-util: 30.4.1 + + express@4.22.2: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.6 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.0.7 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.2 + fresh: 0.5.2 + http-errors: 2.0.1 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.13 + proxy-addr: 2.0.7 + qs: 6.15.3 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.2 + serve-static: 1.16.3 + setprototypeof: 1.2.0 + statuses: 2.0.2 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + express@5.2.1: + dependencies: + accepts: 2.0.0 + body-parser: 2.3.0 + content-disposition: 1.1.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.3(supports-color@7.2.0) + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.1 + fresh: 2.0.0 + http-errors: 2.0.1 + merge-descriptors: 2.0.0 + mime-types: 3.0.2 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.15.3 + range-parser: 1.3.0 + router: 2.2.0 + send: 1.2.1 + serve-static: 2.2.1 + statuses: 2.0.2 + type-is: 2.1.0 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-safe-stringify@2.1.1: {} + + fast-uri@3.1.3: {} + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + faye-websocket@0.11.4: + dependencies: + websocket-driver: 0.7.5 + + fb-watchman@2.0.2: + dependencies: + bser: 2.1.1 + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + figures@3.2.0: + dependencies: + escape-string-regexp: 1.0.5 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + file-type@21.3.4: + dependencies: + '@tokenizer/inflate': 0.4.1 + strtok3: 10.3.5 + token-types: 6.1.2 + uint8array-extras: 1.5.0 + transitivePeerDependencies: + - supports-color + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + finalhandler@1.3.2: + dependencies: + debug: 2.6.9 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + finalhandler@2.1.1: + dependencies: + debug: 4.4.3(supports-color@7.2.0) + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + find-cache-dir@4.0.0: + dependencies: + common-path-prefix: 3.0.0 + pkg-dir: 7.0.0 + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + find-up@6.3.0: + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flat@5.0.2: {} + + flatted@3.4.2: {} + + follow-redirects@1.16.0(debug@4.4.3(supports-color@7.2.0)): + optionalDependencies: + debug: 4.4.3(supports-color@7.2.0) + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + fork-ts-checker-webpack-plugin@9.1.0(typescript@5.9.3)(webpack@5.108.4): + dependencies: + '@babel/code-frame': 7.29.7 + chalk: 4.1.2 + chokidar: 4.0.3 + cosmiconfig: 8.3.6(typescript@5.9.3) + deepmerge: 4.3.1 + fs-extra: 10.1.0 + memfs: 3.5.3 + minimatch: 3.1.5 + node-abort-controller: 3.1.1 + schema-utils: 3.3.0 + semver: 7.8.5 + tapable: 2.3.3 + typescript: 5.9.3 + webpack: 5.108.4(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack-cli@7.2.1) + + form-data@4.0.6: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.4 + mime-types: 2.1.35 + + formidable@3.5.4: + dependencies: + '@paralleldrive/cuid2': 2.3.1 + dezalgo: 1.0.4 + once: 1.4.0 + + forwarded@0.2.0: {} + + fraction.js@5.3.4: {} + + fresh@0.5.2: {} + + fresh@2.0.0: {} + + fs-constants@1.0.0: {} + + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.1 + universalify: 2.0.1 + + fs-monkey@1.1.0: {} + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.6.0: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-package-type@0.1.0: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@6.0.1: {} + + get-them-args@1.3.2: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob-to-regex.js@1.2.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + + glob@10.5.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.9 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.5 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@14.0.0: {} + + globals@17.7.0: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + handle-thing@2.0.1: {} + + handlebars@4.7.9: + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.19.3 + + harmony-reflect@1.6.2: {} + + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + he@1.2.0: {} + + hpack.js@2.1.6: + dependencies: + inherits: 2.0.4 + obuf: 1.1.2 + readable-stream: 2.3.8 + wbuf: 1.7.3 + + html-encoding-sniffer@3.0.0: + dependencies: + whatwg-encoding: 2.0.0 + + html-escaper@2.0.2: {} + + http-deceiver@1.2.7: {} + + http-errors@1.8.1: + dependencies: + depd: 1.1.2 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 1.5.0 + toidentifier: 1.0.1 + + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + + http-parser-js@0.5.10: {} + + http-proxy-middleware@2.0.10(@types/express@4.17.25): + dependencies: + '@types/http-proxy': 1.17.17 + http-proxy: 1.18.1 + is-glob: 4.0.3 + is-plain-obj: 3.0.0 + micromatch: 4.0.8 + optionalDependencies: + '@types/express': 4.17.25 + transitivePeerDependencies: + - debug + + http-proxy@1.18.1: + dependencies: + eventemitter3: 4.0.7 + follow-redirects: 1.16.0(debug@4.4.3(supports-color@7.2.0)) + requires-port: 1.0.0 + transitivePeerDependencies: + - debug + + http-server@14.1.1: + dependencies: + basic-auth: 2.0.1 + chalk: 4.1.2 + corser: 2.0.1 + he: 1.2.0 + html-encoding-sniffer: 3.0.0 + http-proxy: 1.18.1 + mime: 1.6.0 + minimist: 1.2.8 + opener: 1.5.2 + portfinder: 1.0.38 + secure-compare: 3.0.1 + union: 0.5.0 + url-join: 4.0.1 + transitivePeerDependencies: + - debug + - supports-color + + https-proxy-agent@5.0.1(supports-color@7.2.0): + dependencies: + agent-base: 6.0.2(supports-color@7.2.0) + debug: 4.4.3(supports-color@7.2.0) + transitivePeerDependencies: + - supports-color + + human-signals@2.1.0: {} + + husky@9.1.7: {} + + hyperdyperid@1.2.0: {} + + iceberg-js@0.8.1: {} + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.7.3: + dependencies: + safer-buffer: 2.1.2 + + icss-utils@5.1.0(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + + identity-obj-proxy@3.0.0: + dependencies: + harmony-reflect: 1.6.2 + + ieee754@1.2.1: {} + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + ignore@7.0.6: {} + + image-size@0.5.5: + optional: true + + immutable@5.1.9: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-local@3.2.0: + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + interpret@3.1.1: {} + + ip-regex@2.1.0: {} + + ipaddr.js@1.9.1: {} + + ipaddr.js@2.4.0: {} + + is-arrayish@0.2.1: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-core-module@2.16.2: + dependencies: + hasown: 2.0.4 + + is-docker@2.2.1: {} + + is-docker@3.0.0: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-fullwidth-code-point@5.1.0: + dependencies: + get-east-asian-width: 1.6.0 + + is-generator-fn@2.1.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-interactive@1.0.0: {} + + is-network-error@1.3.2: {} + + is-number@7.0.0: {} + + is-plain-obj@3.0.0: {} + + is-plain-object@2.0.4: + dependencies: + isobject: 3.0.1 + + is-promise@4.0.0: {} + + is-stream@2.0.1: {} + + is-unicode-supported@0.1.0: {} + + is-url@1.2.4: {} + + is-what@3.14.1: {} + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + + is-wsl@3.1.1: + dependencies: + is-inside-container: 1.0.0 + + is2@2.0.1: + dependencies: + deep-is: 0.1.4 + ip-regex: 2.1.0 + is-url: 1.2.4 + + isarray@1.0.0: {} + + isexe@2.0.0: {} + + isobject@3.0.1: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-instrument@6.0.3: + dependencies: + '@babel/core': 7.29.7 + '@babel/parser': 7.29.7 + '@istanbuljs/schema': 0.1.6 + istanbul-lib-coverage: 3.2.2 + semver: 7.8.5 + transitivePeerDependencies: + - supports-color + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@5.0.6: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + debug: 4.4.3(supports-color@7.2.0) + istanbul-lib-coverage: 3.2.2 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + iterare@1.2.1: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jest-changed-files@30.3.0: + dependencies: + execa: 5.1.1 + jest-util: 30.3.0 + p-limit: 3.1.0 + + jest-circus@30.3.0(babel-plugin-macros@3.1.0): + dependencies: + '@jest/environment': 30.3.0 + '@jest/expect': 30.3.0 + '@jest/test-result': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 22.20.1 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.7.2(babel-plugin-macros@3.1.0) + is-generator-fn: 2.1.0 + jest-each: 30.3.0 + jest-matcher-utils: 30.3.0 + jest-message-util: 30.3.0 + jest-runtime: 30.3.0 + jest-snapshot: 30.3.0 + jest-util: 30.3.0 + p-limit: 3.1.0 + pretty-format: 30.3.0 + pure-rand: 7.0.1 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-circus@30.4.2(babel-plugin-macros@3.1.0): + dependencies: + '@jest/environment': 30.4.1 + '@jest/expect': 30.4.1 + '@jest/test-result': 30.4.1 + '@jest/types': 30.4.1 + '@types/node': 22.20.1 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.7.2(babel-plugin-macros@3.1.0) + is-generator-fn: 2.1.0 + jest-each: 30.4.1 + jest-matcher-utils: 30.4.1 + jest-message-util: 30.4.1 + jest-runtime: 30.4.2 + jest-snapshot: 30.4.1 + jest-util: 30.4.1 + p-limit: 3.1.0 + pretty-format: 30.4.1 + pure-rand: 7.0.1 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-cli@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)): + dependencies: + '@jest/core': 30.3.0(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)) + '@jest/test-result': 30.3.0 + '@jest/types': 30.3.0 + chalk: 4.1.2 + exit-x: 0.2.2 + import-local: 3.2.0 + jest-config: 30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)) + jest-util: 30.3.0 + jest-validate: 30.3.0 + yargs: 17.7.3 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + + jest-config@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)): + dependencies: + '@babel/core': 7.29.7 + '@jest/get-type': 30.1.0 + '@jest/pattern': 30.0.1 + '@jest/test-sequencer': 30.3.0 + '@jest/types': 30.3.0 + babel-jest: 30.3.0(@babel/core@7.29.7) + chalk: 4.1.2 + ci-info: 4.4.0 + deepmerge: 4.3.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-circus: 30.3.0(babel-plugin-macros@3.1.0) + jest-docblock: 30.2.0 + jest-environment-node: 30.3.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.3.0 + jest-runner: 30.3.0 + jest-util: 30.3.0 + jest-validate: 30.3.0 + parse-json: 5.2.0 + pretty-format: 30.3.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 22.20.1 + ts-node: 10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-config@30.4.2(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)): + dependencies: + '@babel/core': 7.29.7 + '@jest/get-type': 30.1.0 + '@jest/pattern': 30.4.0 + '@jest/test-sequencer': 30.4.1 + '@jest/types': 30.4.1 + babel-jest: 30.4.1(@babel/core@7.29.7) + chalk: 4.1.2 + ci-info: 4.4.0 + deepmerge: 4.3.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-circus: 30.4.2(babel-plugin-macros@3.1.0) + jest-docblock: 30.4.0 + jest-environment-node: 30.4.1 + jest-regex-util: 30.4.0 + jest-resolve: 30.4.1 + jest-runner: 30.4.2 + jest-util: 30.4.1 + jest-validate: 30.4.1 + parse-json: 5.2.0 + pretty-format: 30.4.1 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 22.20.1 + ts-node: 10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-diff@30.3.0: + dependencies: + '@jest/diff-sequences': 30.3.0 + '@jest/get-type': 30.1.0 + chalk: 4.1.2 + pretty-format: 30.3.0 + + jest-diff@30.4.1: + dependencies: + '@jest/diff-sequences': 30.4.0 + '@jest/get-type': 30.1.0 + chalk: 4.1.2 + pretty-format: 30.4.1 + + jest-docblock@30.2.0: + dependencies: + detect-newline: 3.1.0 + + jest-docblock@30.4.0: + dependencies: + detect-newline: 3.1.0 + + jest-each@30.3.0: + dependencies: + '@jest/get-type': 30.1.0 + '@jest/types': 30.3.0 + chalk: 4.1.2 + jest-util: 30.3.0 + pretty-format: 30.3.0 + + jest-each@30.4.1: + dependencies: + '@jest/get-type': 30.1.0 + '@jest/types': 30.4.1 + chalk: 4.1.2 + jest-util: 30.4.1 + pretty-format: 30.4.1 + + jest-environment-node@30.3.0: + dependencies: + '@jest/environment': 30.3.0 + '@jest/fake-timers': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 22.20.1 + jest-mock: 30.3.0 + jest-util: 30.3.0 + jest-validate: 30.3.0 + + jest-environment-node@30.4.1: + dependencies: + '@jest/environment': 30.4.1 + '@jest/fake-timers': 30.4.1 + '@jest/types': 30.4.1 + '@types/node': 22.20.1 + jest-mock: 30.4.1 + jest-util: 30.4.1 + jest-validate: 30.4.1 + + jest-haste-map@30.3.0: + dependencies: + '@jest/types': 30.3.0 + '@types/node': 22.20.1 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 30.0.1 + jest-util: 30.3.0 + jest-worker: 30.3.0 + picomatch: 4.0.5 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + + jest-haste-map@30.4.1: + dependencies: + '@jest/types': 30.4.1 + '@types/node': 22.20.1 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 30.4.0 + jest-util: 30.4.1 + jest-worker: 30.4.1 + picomatch: 4.0.5 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + + jest-leak-detector@30.3.0: + dependencies: + '@jest/get-type': 30.1.0 + pretty-format: 30.3.0 + + jest-leak-detector@30.4.1: + dependencies: + '@jest/get-type': 30.1.0 + pretty-format: 30.4.1 + + jest-matcher-utils@30.3.0: + dependencies: + '@jest/get-type': 30.1.0 + chalk: 4.1.2 + jest-diff: 30.3.0 + pretty-format: 30.3.0 + + jest-matcher-utils@30.4.1: + dependencies: + '@jest/get-type': 30.1.0 + chalk: 4.1.2 + jest-diff: 30.4.1 + pretty-format: 30.4.1 + + jest-message-util@30.3.0: + dependencies: + '@babel/code-frame': 7.29.7 + '@jest/types': 30.3.0 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + picomatch: 4.0.5 + pretty-format: 30.3.0 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-message-util@30.4.1: + dependencies: + '@babel/code-frame': 7.29.7 + '@jest/types': 30.4.1 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-util: 30.4.1 + picomatch: 4.0.5 + pretty-format: 30.4.1 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-mock@30.3.0: + dependencies: + '@jest/types': 30.3.0 + '@types/node': 22.20.1 + jest-util: 30.3.0 + + jest-mock@30.4.1: + dependencies: + '@jest/types': 30.4.1 + '@types/node': 22.20.1 + jest-util: 30.4.1 + + jest-pnp-resolver@1.2.3(jest-resolve@30.3.0): + optionalDependencies: + jest-resolve: 30.3.0 + + jest-pnp-resolver@1.2.3(jest-resolve@30.4.1): + optionalDependencies: + jest-resolve: 30.4.1 + + jest-regex-util@30.0.1: {} + + jest-regex-util@30.4.0: {} + + jest-resolve-dependencies@30.3.0: + dependencies: + jest-regex-util: 30.0.1 + jest-snapshot: 30.3.0 + transitivePeerDependencies: + - supports-color + + jest-resolve@30.3.0: + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 30.3.0 + jest-pnp-resolver: 1.2.3(jest-resolve@30.3.0) + jest-util: 30.3.0 + jest-validate: 30.3.0 + slash: 3.0.0 + unrs-resolver: 1.12.2 + + jest-resolve@30.4.1: + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 30.4.1 + jest-pnp-resolver: 1.2.3(jest-resolve@30.4.1) + jest-util: 30.4.1 + jest-validate: 30.4.1 + slash: 3.0.0 + unrs-resolver: 1.12.2 + + jest-runner@30.3.0: + dependencies: + '@jest/console': 30.3.0 + '@jest/environment': 30.3.0 + '@jest/test-result': 30.3.0 + '@jest/transform': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 22.20.1 + chalk: 4.1.2 + emittery: 0.13.1 + exit-x: 0.2.2 + graceful-fs: 4.2.11 + jest-docblock: 30.2.0 + jest-environment-node: 30.3.0 + jest-haste-map: 30.3.0 + jest-leak-detector: 30.3.0 + jest-message-util: 30.3.0 + jest-resolve: 30.3.0 + jest-runtime: 30.3.0 + jest-util: 30.3.0 + jest-watcher: 30.3.0 + jest-worker: 30.3.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + + jest-runner@30.4.2: + dependencies: + '@jest/console': 30.4.1 + '@jest/environment': 30.4.1 + '@jest/test-result': 30.4.1 + '@jest/transform': 30.4.1 + '@jest/types': 30.4.1 + '@types/node': 22.20.1 + chalk: 4.1.2 + emittery: 0.13.1 + exit-x: 0.2.2 + graceful-fs: 4.2.11 + jest-docblock: 30.4.0 + jest-environment-node: 30.4.1 + jest-haste-map: 30.4.1 + jest-leak-detector: 30.4.1 + jest-message-util: 30.4.1 + jest-resolve: 30.4.1 + jest-runtime: 30.4.2 + jest-util: 30.4.1 + jest-watcher: 30.4.1 + jest-worker: 30.4.1 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + + jest-runtime@30.3.0: + dependencies: + '@jest/environment': 30.3.0 + '@jest/fake-timers': 30.3.0 + '@jest/globals': 30.3.0 + '@jest/source-map': 30.0.1 + '@jest/test-result': 30.3.0 + '@jest/transform': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 22.20.1 + chalk: 4.1.2 + cjs-module-lexer: 2.2.0 + collect-v8-coverage: 1.0.3 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-haste-map: 30.3.0 + jest-message-util: 30.3.0 + jest-mock: 30.3.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.3.0 + jest-snapshot: 30.3.0 + jest-util: 30.3.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + + jest-runtime@30.4.2: + dependencies: + '@jest/environment': 30.4.1 + '@jest/fake-timers': 30.4.1 + '@jest/globals': 30.4.1 + '@jest/source-map': 30.0.1 + '@jest/test-result': 30.4.1 + '@jest/transform': 30.4.1 + '@jest/types': 30.4.1 + '@types/node': 22.20.1 + chalk: 4.1.2 + cjs-module-lexer: 2.2.0 + collect-v8-coverage: 1.0.3 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-haste-map: 30.4.1 + jest-message-util: 30.4.1 + jest-mock: 30.4.1 + jest-regex-util: 30.4.0 + jest-resolve: 30.4.1 + jest-snapshot: 30.4.1 + jest-util: 30.4.1 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + + jest-snapshot@30.3.0: + dependencies: + '@babel/core': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) + '@babel/types': 7.29.7 + '@jest/expect-utils': 30.3.0 + '@jest/get-type': 30.1.0 + '@jest/snapshot-utils': 30.3.0 + '@jest/transform': 30.3.0 + '@jest/types': 30.3.0 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.7) + chalk: 4.1.2 + expect: 30.3.0 + graceful-fs: 4.2.11 + jest-diff: 30.3.0 + jest-matcher-utils: 30.3.0 + jest-message-util: 30.3.0 + jest-util: 30.3.0 + pretty-format: 30.3.0 + semver: 7.8.5 + synckit: 0.11.13 + transitivePeerDependencies: + - supports-color + + jest-snapshot@30.4.1: + dependencies: + '@babel/core': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) + '@babel/types': 7.29.7 + '@jest/expect-utils': 30.4.1 + '@jest/get-type': 30.1.0 + '@jest/snapshot-utils': 30.4.1 + '@jest/transform': 30.4.1 + '@jest/types': 30.4.1 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.7) + chalk: 4.1.2 + expect: 30.4.1 + graceful-fs: 4.2.11 + jest-diff: 30.4.1 + jest-matcher-utils: 30.4.1 + jest-message-util: 30.4.1 + jest-util: 30.4.1 + pretty-format: 30.4.1 + semver: 7.8.5 + synckit: 0.11.13 + transitivePeerDependencies: + - supports-color + + jest-util@30.3.0: + dependencies: + '@jest/types': 30.3.0 + '@types/node': 22.20.1 + chalk: 4.1.2 + ci-info: 4.4.0 + graceful-fs: 4.2.11 + picomatch: 4.0.5 + + jest-util@30.4.1: + dependencies: + '@jest/types': 30.4.1 + '@types/node': 22.20.1 + chalk: 4.1.2 + ci-info: 4.4.0 + graceful-fs: 4.2.11 + picomatch: 4.0.5 + + jest-validate@30.3.0: + dependencies: + '@jest/get-type': 30.1.0 + '@jest/types': 30.3.0 + camelcase: 6.3.0 + chalk: 4.1.2 + leven: 3.1.0 + pretty-format: 30.3.0 + + jest-validate@30.4.1: + dependencies: + '@jest/get-type': 30.1.0 + '@jest/types': 30.4.1 + camelcase: 6.3.0 + chalk: 4.1.2 + leven: 3.1.0 + pretty-format: 30.4.1 + + jest-watcher@30.3.0: + dependencies: + '@jest/test-result': 30.3.0 + '@jest/types': 30.3.0 + '@types/node': 22.20.1 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 30.3.0 + string-length: 4.0.2 + + jest-watcher@30.4.1: + dependencies: + '@jest/test-result': 30.4.1 + '@jest/types': 30.4.1 + '@types/node': 22.20.1 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 30.4.1 + string-length: 4.0.2 + + jest-worker@27.5.1: + dependencies: + '@types/node': 22.20.1 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest-worker@30.3.0: + dependencies: + '@types/node': 22.20.1 + '@ungap/structured-clone': 1.3.3 + jest-util: 30.3.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest-worker@30.4.1: + dependencies: + '@types/node': 22.20.1 + '@ungap/structured-clone': 1.3.3 + jest-util: 30.4.1 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)): + dependencies: + '@jest/core': 30.3.0(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)) + '@jest/types': 30.3.0 + import-local: 3.2.0 + jest-cli: 30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + + jiti@2.7.0: {} + + jose@6.2.3: {} + + js-tokens@4.0.0: {} + + js-yaml@3.15.0: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.3.0: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@2.2.3: {} + + jsonc-eslint-parser@2.4.2: + dependencies: + acorn: 8.17.0 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + semver: 7.8.5 + + jsonc-parser@3.3.1: {} + + jsonfile@6.2.1: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kill-port@1.6.1: + dependencies: + get-them-args: 1.3.2 + shell-exec: 1.0.2 + + kind-of@6.0.3: {} + + launch-editor@2.14.1: + dependencies: + picocolors: 1.1.1 + shell-quote: 1.10.0 + + less-loader@12.3.3(less@4.5.1)(webpack@5.108.4): + dependencies: + less: 4.5.1 + optionalDependencies: + webpack: 5.108.4(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack-cli@7.2.1) + + less@4.5.1: + dependencies: + copy-anything: 2.0.6 + parse-node-version: 1.0.1 + tslib: 2.8.1 + optionalDependencies: + errno: 0.1.8 + graceful-fs: 4.2.11 + image-size: 0.5.5 + make-dir: 2.1.0 + mime: 1.6.0 + needle: 3.5.0 + source-map: 0.6.1 + + leven@3.1.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + libphonenumber-js@1.13.8: {} + + license-webpack-plugin@4.0.2(webpack@5.108.4): + dependencies: + webpack-sources: 3.5.1 + optionalDependencies: + webpack: 5.108.4(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack-cli@7.2.1) + + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + lines-and-columns@2.0.3: {} + + lint-staged@17.0.8: + dependencies: + listr2: 10.2.2 + picomatch: 4.0.5 + string-argv: 0.3.2 + tinyexec: 1.2.4 + optionalDependencies: + yaml: 2.9.0 + + listr2@10.2.2: + dependencies: + cli-truncate: 5.2.0 + eventemitter3: 5.0.4 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 10.0.0 + + load-esm@1.0.3: {} + + loader-runner@4.3.2: {} + + loader-utils@2.0.4: + dependencies: + big.js: 5.2.2 + emojis-list: 3.0.0 + json5: 2.2.3 + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + locate-path@7.2.0: + dependencies: + p-locate: 6.0.0 + + lodash.debounce@4.0.8: {} + + lodash.memoize@4.1.2: {} + + lodash.merge@4.6.2: {} + + lodash.uniq@4.5.0: {} + + lodash@4.18.1: {} + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.3.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.2 + strip-ansi: 7.2.0 + wrap-ansi: 9.0.2 + + lru-cache@10.4.3: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + luxon@3.7.2: {} + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + make-dir@2.1.0: + dependencies: + pify: 4.0.1 + semver: 5.7.2 + optional: true + + make-dir@4.0.0: + dependencies: + semver: 7.8.5 + + make-error@1.3.6: {} + + makeerror@1.0.12: + dependencies: + tmpl: 1.0.5 + + math-intrinsics@1.1.0: {} + + mdn-data@2.0.28: {} + + mdn-data@2.27.1: {} + + media-typer@0.3.0: {} + + media-typer@1.1.0: {} + + memfs@3.5.3: + dependencies: + fs-monkey: 1.1.0 + + memfs@4.64.0(tslib@2.8.1): + dependencies: + '@jsonjoy.com/fs-core': 4.64.0(tslib@2.8.1) + '@jsonjoy.com/fs-fsa': 4.64.0(tslib@2.8.1) + '@jsonjoy.com/fs-node': 4.64.0(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.64.0(tslib@2.8.1) + '@jsonjoy.com/fs-node-to-fsa': 4.64.0(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.64.0(tslib@2.8.1) + '@jsonjoy.com/fs-print': 4.64.0(tslib@2.8.1) + '@jsonjoy.com/fs-snapshot': 4.64.0(tslib@2.8.1) + '@jsonjoy.com/json-pack': 1.21.0(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + glob-to-regex.js: 1.2.0(tslib@2.8.1) + thingies: 2.6.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + merge-descriptors@1.0.3: {} + + merge-descriptors@2.0.0: {} + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + methods@1.1.2: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + mime-db@1.52.0: {} + + mime-db@1.54.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + + mime@1.6.0: {} + + mime@2.6.0: {} + + mimic-fn@2.1.0: {} + + mimic-function@5.0.1: {} + + mini-css-extract-plugin@2.4.7(webpack@5.108.4): + dependencies: + schema-utils: 4.3.3 + webpack: 5.108.4(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack-cli@7.2.1) + + minimalistic-assert@1.0.1: {} + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.7 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.16 + + minimatch@7.4.9: + dependencies: + brace-expansion: 2.1.2 + + minimatch@9.0.9: + dependencies: + brace-expansion: 2.1.2 + + minimist@1.2.8: {} + + minimizer-webpack-plugin@5.6.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack@5.108.4): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + jest-worker: 27.5.1 + schema-utils: 4.3.3 + terser: 5.49.0 + webpack: 5.108.4(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack-cli@7.2.1) + optionalDependencies: + '@swc/core': 1.15.43(@swc/helpers@0.5.23) + postcss: 8.5.18 + + minipass@7.1.3: {} + + mkdirp@2.1.6: {} + + ms@2.0.0: {} + + ms@2.1.2: {} + + ms@2.1.3: {} + + multer@2.2.0: + dependencies: + append-field: 1.0.0 + busboy: 1.6.0 + concat-stream: 2.0.0 + type-is: 1.6.18 + + multicast-dns@7.2.5: + dependencies: + dns-packet: 5.6.1 + thunky: 1.1.0 + + nanoid@3.3.16: {} + + napi-postinstall@0.3.4: {} + + natural-compare@1.4.0: {} + + needle@3.5.0: + dependencies: + iconv-lite: 0.6.3 + sax: 1.6.0 + optional: true + + negotiator@0.6.3: {} + + negotiator@0.6.4: {} + + negotiator@1.0.0: {} + + neo-async@2.6.2: {} + + node-abort-controller@3.1.1: {} + + node-addon-api@7.1.1: + optional: true + + node-int64@0.4.0: {} + + node-releases@2.0.51: {} + + normalize-path@3.0.0: {} + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + nx@23.0.2(@swc/core@1.15.43(@swc/helpers@0.5.23)): + dependencies: + '@emnapi/core': 1.4.5 + '@emnapi/runtime': 1.4.5 + '@emnapi/wasi-threads': 1.0.4 + '@jest/diff-sequences': 30.0.1 + '@napi-rs/wasm-runtime': 0.2.4 + '@tybys/wasm-util': 0.9.0 + '@yarnpkg/lockfile': 1.1.0 + '@zkochan/js-yaml': 0.0.7 + agent-base: 6.0.2(supports-color@7.2.0) + ansi-colors: 4.1.3 + ansi-regex: 5.0.1 + ansi-styles: 4.3.0 + argparse: 2.0.1 + asynckit: 0.4.0 + axios: 1.16.1(debug@4.4.3(supports-color@7.2.0))(supports-color@7.2.0) + balanced-match: 4.0.3 + base64-js: 1.5.1 + bl: 4.1.0 + brace-expansion: 5.0.6 + buffer: 5.7.1 + call-bind-apply-helpers: 1.0.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.6.1 + cliui: 8.0.1 + clone: 1.0.4 + color-convert: 2.0.1 + color-name: 1.1.4 + combined-stream: 1.0.8 + debug: 4.4.3(supports-color@7.2.0) + defaults: 1.0.4 + define-lazy-prop: 2.0.0 + delayed-stream: 1.0.0 + dotenv: 16.4.7 + dotenv-expand: 12.0.3 + dunder-proto: 1.0.1 + ejs: 5.0.1 + emoji-regex: 8.0.0 + end-of-stream: 1.4.5 + enquirer: 2.3.6 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + escalade: 3.2.0 + escape-string-regexp: 1.0.5 + figures: 3.2.0 + flat: 5.0.2 + follow-redirects: 1.16.0(debug@4.4.3(supports-color@7.2.0)) + form-data: 4.0.6 + fs-constants: 1.0.0 + function-bind: 1.1.2 + get-caller-file: 2.0.5 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + has-flag: 4.0.0 + has-symbols: 1.1.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + https-proxy-agent: 5.0.1(supports-color@7.2.0) + ieee754: 1.2.1 + ignore: 7.0.5 + inherits: 2.0.4 + is-docker: 2.2.1 + is-fullwidth-code-point: 3.0.0 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + is-wsl: 2.2.0 + isexe: 2.0.0 + json5: 2.2.3 + jsonc-parser: 3.3.1 + lines-and-columns: 2.0.3 + log-symbols: 4.1.0 + math-intrinsics: 1.1.0 + mime-db: 1.52.0 + mime-types: 2.1.35 + mimic-fn: 2.1.0 + minimatch: 10.2.5 + minimist: 1.2.8 + ms: 2.1.3 + npm-run-path: 4.0.1 + once: 1.4.0 + onetime: 5.1.2 + open: 8.4.2 + ora: 5.4.1 + path-key: 3.1.1 + picocolors: 1.1.1 + proxy-from-env: 2.1.0 + readable-stream: 3.6.2 + require-directory: 2.1.1 + resolve.exports: 2.0.3 + restore-cursor: 3.1.0 + safe-buffer: 5.2.1 + semver: 7.7.4 + signal-exit: 3.0.7 + smol-toml: 1.6.1 + string-width: 4.2.3 + string_decoder: 1.3.0 + strip-ansi: 6.0.1 + strip-bom: 3.0.0 + supports-color: 7.2.0 + tar-stream: 2.2.0 + tmp: 0.2.7 + tsconfig-paths: 4.2.0 + tslib: 2.8.1 + util-deprecate: 1.0.2 + wcwidth: 1.0.1 + which: 3.0.1 + wrap-ansi: 7.0.0 + wrappy: 1.0.2 + y18n: 5.0.8 + yaml: 2.9.0 + yargs: 17.7.2 + yargs-parser: 21.1.1 + optionalDependencies: + '@nx/nx-darwin-arm64': 23.0.2 + '@nx/nx-darwin-x64': 23.0.2 + '@nx/nx-freebsd-x64': 23.0.2 + '@nx/nx-linux-arm-gnueabihf': 23.0.2 + '@nx/nx-linux-arm64-gnu': 23.0.2 + '@nx/nx-linux-arm64-musl': 23.0.2 + '@nx/nx-linux-x64-gnu': 23.0.2 + '@nx/nx-linux-x64-musl': 23.0.2 + '@nx/nx-win32-arm64-msvc': 23.0.2 + '@nx/nx-win32-x64-msvc': 23.0.2 + '@swc/core': 1.15.43(@swc/helpers@0.5.23) + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + obuf@1.1.2: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + on-headers@1.1.0: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + open@10.2.0: + dependencies: + default-browser: 5.5.0 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + wsl-utils: 0.1.0 + + open@8.4.2: + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + + opener@1.5.2: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + ora@5.4.1: + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.6.1 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-limit@4.0.0: + dependencies: + yocto-queue: 1.2.2 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-locate@6.0.0: + dependencies: + p-limit: 4.0.0 + + p-retry@6.2.1: + dependencies: + '@types/retry': 0.12.2 + is-network-error: 1.3.2 + retry: 0.13.1 + + p-try@2.2.0: {} + + package-json-from-dist@1.0.1: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.7 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse-node-version@1.0.1: {} + + parse5@4.0.0: {} + + parseurl@1.3.3: {} + + passport-headerapikey@1.2.2: + dependencies: + lodash: 4.18.1 + passport-strategy: 1.0.0 + + passport-http-bearer@1.0.1: + dependencies: + passport-strategy: 1.0.0 + + passport-strategy@1.0.0: {} + + passport@0.7.0: + dependencies: + passport-strategy: 1.0.0 + pause: 0.0.1 + utils-merge: 1.0.1 + + path-browserify@1.0.1: {} + + path-exists@4.0.0: {} + + path-exists@5.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.3 + + path-to-regexp@0.1.13: {} + + path-to-regexp@8.4.2: {} + + path-type@4.0.0: {} + + pause@0.0.1: {} + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.4: {} + + picomatch@4.0.5: {} + + pify@2.3.0: {} + + pify@4.0.1: + optional: true + + pirates@4.0.7: {} + + pkg-dir@4.2.0: + dependencies: + find-up: 4.1.0 + + pkg-dir@7.0.0: + dependencies: + find-up: 6.3.0 + + pkijs@3.4.0: + dependencies: + '@noble/hashes': 1.4.0 + asn1js: 3.0.10 + bytestreamjs: 2.0.1 + pvtsutils: 1.3.6 + pvutils: 1.1.5 + tslib: 2.8.1 + + pluralize@8.0.0: {} + + portfinder@1.0.38: + dependencies: + async: 3.2.6 + debug: 4.4.3(supports-color@7.2.0) + transitivePeerDependencies: + - supports-color + + postcss-calc@10.1.1(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + postcss-selector-parser: 7.1.4 + postcss-value-parser: 4.2.0 + + postcss-colormin@7.0.10(postcss@8.5.18): + dependencies: + '@colordx/core': 5.5.0 + browserslist: 4.28.6 + caniuse-api: 3.0.0 + postcss: 8.5.18 + postcss-value-parser: 4.2.0 + + postcss-convert-values@7.0.12(postcss@8.5.18): + dependencies: + browserslist: 4.28.6 + postcss: 8.5.18 + postcss-value-parser: 4.2.0 + + postcss-discard-comments@7.0.8(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + postcss-selector-parser: 7.1.4 + + postcss-discard-duplicates@7.0.4(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + + postcss-discard-empty@7.0.3(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + + postcss-discard-overridden@7.0.3(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + + postcss-import@14.1.0(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.12 + + postcss-loader@8.2.1(postcss@8.5.18)(typescript@5.9.3)(webpack@5.108.4): + dependencies: + cosmiconfig: 9.0.2(typescript@5.9.3) + jiti: 2.7.0 + postcss: 8.5.18 + semver: 7.8.5 + optionalDependencies: + webpack: 5.108.4(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack-cli@7.2.1) + transitivePeerDependencies: + - typescript + + postcss-merge-longhand@7.0.7(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + postcss-value-parser: 4.2.0 + stylehacks: 7.0.11(postcss@8.5.18) + + postcss-merge-rules@7.0.11(postcss@8.5.18): + dependencies: + browserslist: 4.28.6 + caniuse-api: 3.0.0 + cssnano-utils: 5.0.3(postcss@8.5.18) + postcss: 8.5.18 + postcss-selector-parser: 7.1.4 + + postcss-minify-font-values@7.0.3(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + postcss-value-parser: 4.2.0 + + postcss-minify-gradients@7.0.5(postcss@8.5.18): + dependencies: + '@colordx/core': 5.5.0 + cssnano-utils: 5.0.3(postcss@8.5.18) + postcss: 8.5.18 + postcss-value-parser: 4.2.0 + + postcss-minify-params@7.0.9(postcss@8.5.18): + dependencies: + browserslist: 4.28.6 + cssnano-utils: 5.0.3(postcss@8.5.18) + postcss: 8.5.18 + postcss-value-parser: 4.2.0 + + postcss-minify-selectors@7.1.2(postcss@8.5.18): + dependencies: + browserslist: 4.28.6 + caniuse-api: 3.0.0 + cssesc: 3.0.0 + postcss: 8.5.18 + postcss-selector-parser: 7.1.4 + + postcss-modules-extract-imports@3.1.0(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + + postcss-modules-local-by-default@4.2.0(postcss@8.5.18): + dependencies: + icss-utils: 5.1.0(postcss@8.5.18) + postcss: 8.5.18 + postcss-selector-parser: 7.1.4 + postcss-value-parser: 4.2.0 + + postcss-modules-scope@3.2.1(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + postcss-selector-parser: 7.1.4 + + postcss-modules-values@4.0.0(postcss@8.5.18): + dependencies: + icss-utils: 5.1.0(postcss@8.5.18) + postcss: 8.5.18 + + postcss-normalize-charset@7.0.3(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + + postcss-normalize-display-values@7.0.3(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + postcss-value-parser: 4.2.0 + + postcss-normalize-positions@7.0.4(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + postcss-value-parser: 4.2.0 + + postcss-normalize-repeat-style@7.0.4(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + postcss-value-parser: 4.2.0 + + postcss-normalize-string@7.0.3(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + postcss-value-parser: 4.2.0 + + postcss-normalize-timing-functions@7.0.3(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + postcss-value-parser: 4.2.0 + + postcss-normalize-unicode@7.0.9(postcss@8.5.18): + dependencies: + browserslist: 4.28.6 + postcss: 8.5.18 + postcss-value-parser: 4.2.0 + + postcss-normalize-url@7.0.3(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + postcss-value-parser: 4.2.0 + + postcss-normalize-whitespace@7.0.3(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + postcss-value-parser: 4.2.0 + + postcss-ordered-values@7.0.4(postcss@8.5.18): + dependencies: + cssnano-utils: 5.0.3(postcss@8.5.18) + postcss: 8.5.18 + postcss-value-parser: 4.2.0 + + postcss-reduce-initial@7.0.9(postcss@8.5.18): + dependencies: + browserslist: 4.28.6 + caniuse-api: 3.0.0 + postcss: 8.5.18 + + postcss-reduce-transforms@7.0.3(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + postcss-value-parser: 4.2.0 + + postcss-selector-parser@7.1.4: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-svgo@7.1.3(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + postcss-value-parser: 4.2.0 + svgo: 4.0.2 + + postcss-unique-selectors@7.0.7(postcss@8.5.18): + dependencies: + postcss: 8.5.18 + postcss-selector-parser: 7.1.4 + + postcss-value-parser@4.2.0: {} + + postcss@8.5.18: + dependencies: + nanoid: 3.3.16 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier@3.9.5: {} + + pretty-format@30.3.0: + dependencies: + '@jest/schemas': 30.0.5 + ansi-styles: 5.2.0 + react-is: 18.3.1 + + pretty-format@30.4.1: + dependencies: + '@jest/schemas': 30.4.1 + ansi-styles: 5.2.0 + react-is-18: react-is@18.3.1 + react-is-19: react-is@19.2.7 + + process-nextick-args@2.0.1: {} + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + proxy-from-env@2.1.0: {} + + prr@1.0.1: + optional: true + + punycode@2.3.1: {} + + pure-rand@7.0.1: {} + + pvtsutils@1.3.6: + dependencies: + tslib: 2.8.1 + + pvutils@1.1.5: {} + + qs@6.15.3: + dependencies: + es-define-property: 1.0.1 + side-channel: 1.1.1 + + queue-microtask@1.2.3: {} + + ramda@0.31.3: {} + + range-parser@1.2.1: {} + + range-parser@1.3.0: {} + + raw-body@2.5.3: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + raw-body@3.0.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.3 + unpipe: 1.0.0 + + react-is@18.3.1: {} + + react-is@19.2.7: {} + + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.2 + + readdirp@4.1.2: {} + + readdirp@5.0.0: {} + + rechoir@0.8.0: + dependencies: + resolve: 1.22.12 + + reflect-metadata@0.2.2: {} + + regenerate-unicode-properties@10.2.2: + dependencies: + regenerate: 1.4.2 + + regenerate@1.4.2: {} + + regexpu-core@6.4.0: + dependencies: + regenerate: 1.4.2 + regenerate-unicode-properties: 10.2.2 + regjsgen: 0.8.0 + regjsparser: 0.13.2 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.2.1 + + regjsgen@0.8.0: {} + + regjsparser@0.13.2: + dependencies: + jsesc: 3.1.0 + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + requires-port@1.0.0: {} + + resolve-cwd@3.0.0: + dependencies: + resolve-from: 5.0.0 + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve.exports@2.0.3: {} + + resolve@1.22.12: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.2 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + retry@0.13.1: {} + + reusify@1.1.0: {} + + rfdc@1.4.1: {} + + router@2.2.0: + dependencies: + debug: 4.4.3(supports-color@7.2.0) + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.4.2 + transitivePeerDependencies: + - supports-color + + run-applescript@7.1.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + rxjs@7.8.1: + dependencies: + tslib: 2.8.1 + + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + + safer-buffer@2.1.2: {} + + sass-embedded-all-unknown@1.100.0: + dependencies: + sass: 1.100.0 + optional: true + + sass-embedded-android-arm64@1.100.0: + optional: true + + sass-embedded-android-arm@1.100.0: + optional: true + + sass-embedded-android-riscv64@1.100.0: + optional: true + + sass-embedded-android-x64@1.100.0: + optional: true + + sass-embedded-darwin-arm64@1.100.0: + optional: true + + sass-embedded-darwin-x64@1.100.0: + optional: true + + sass-embedded-linux-arm64@1.100.0: + optional: true + + sass-embedded-linux-arm@1.100.0: + optional: true + + sass-embedded-linux-musl-arm64@1.100.0: + optional: true + + sass-embedded-linux-musl-arm@1.100.0: + optional: true + + sass-embedded-linux-musl-riscv64@1.100.0: + optional: true + + sass-embedded-linux-musl-x64@1.100.0: + optional: true + + sass-embedded-linux-riscv64@1.100.0: + optional: true + + sass-embedded-linux-x64@1.100.0: + optional: true + + sass-embedded-unknown-all@1.100.0: + dependencies: + sass: 1.100.0 + optional: true + + sass-embedded-win32-arm64@1.100.0: + optional: true + + sass-embedded-win32-x64@1.100.0: + optional: true + + sass-embedded@1.100.0: + dependencies: + '@bufbuild/protobuf': 2.12.1 + colorjs.io: 0.5.2 + immutable: 5.1.9 + rxjs: 7.8.2 + supports-color: 8.1.1 + sync-child-process: 1.0.2 + varint: 6.0.0 + optionalDependencies: + sass-embedded-all-unknown: 1.100.0 + sass-embedded-android-arm: 1.100.0 + sass-embedded-android-arm64: 1.100.0 + sass-embedded-android-riscv64: 1.100.0 + sass-embedded-android-x64: 1.100.0 + sass-embedded-darwin-arm64: 1.100.0 + sass-embedded-darwin-x64: 1.100.0 + sass-embedded-linux-arm: 1.100.0 + sass-embedded-linux-arm64: 1.100.0 + sass-embedded-linux-musl-arm: 1.100.0 + sass-embedded-linux-musl-arm64: 1.100.0 + sass-embedded-linux-musl-riscv64: 1.100.0 + sass-embedded-linux-musl-x64: 1.100.0 + sass-embedded-linux-riscv64: 1.100.0 + sass-embedded-linux-x64: 1.100.0 + sass-embedded-unknown-all: 1.100.0 + sass-embedded-win32-arm64: 1.100.0 + sass-embedded-win32-x64: 1.100.0 + + sass-loader@16.0.8(sass-embedded@1.100.0)(sass@1.101.0)(webpack@5.108.4): + dependencies: + neo-async: 2.6.2 + optionalDependencies: + sass: 1.101.0 + sass-embedded: 1.100.0 + webpack: 5.108.4(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack-cli@7.2.1) + + sass@1.100.0: + dependencies: + chokidar: 5.0.0 + immutable: 5.1.9 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.6 + optional: true + + sass@1.101.0: + dependencies: + chokidar: 5.0.0 + immutable: 5.1.9 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.6 + + sax@1.6.0: {} + + schema-utils@3.3.0: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 6.15.0 + ajv-keywords: 3.5.2(ajv@6.15.0) + + schema-utils@4.3.3: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.20.0 + ajv-formats: 2.1.1(ajv@8.20.0) + ajv-keywords: 5.1.0(ajv@8.20.0) + + secure-compare@3.0.1: {} + + select-hose@2.0.0: {} + + selfsigned@5.5.0: + dependencies: + '@peculiar/x509': 1.14.3 + pkijs: 3.4.0 + + semver@5.7.2: + optional: true + + semver@6.3.1: {} + + semver@7.7.4: {} + + semver@7.8.5: {} + + send@0.19.2: + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.1 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + send@1.2.1: + dependencies: + debug: 4.4.3(supports-color@7.2.0) + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.3.0 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serialize-javascript@7.0.7: {} + + serve-index@1.9.2: + dependencies: + accepts: 1.3.8 + batch: 0.6.1 + debug: 2.6.9 + escape-html: 1.0.3 + http-errors: 1.8.1 + mime-types: 2.1.35 + parseurl: 1.3.3 + transitivePeerDependencies: + - supports-color + + serve-static@1.16.3: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.19.2 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.1: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.1 + transitivePeerDependencies: + - supports-color + + setprototypeof@1.2.0: {} + + shallow-clone@3.0.1: + dependencies: + kind-of: 6.0.3 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shell-exec@1.0.2: {} + + shell-quote@1.10.0: {} + + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + slash@3.0.0: {} + + slice-ansi@7.1.2: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + slice-ansi@8.0.0: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + smol-toml@1.6.1: {} + + sockjs@0.3.24: + dependencies: + faye-websocket: 0.11.4 + uuid: 8.3.2 + websocket-driver: 0.7.5 + + source-map-js@1.2.1: {} + + source-map-loader@5.0.0(webpack@5.108.4): + dependencies: + iconv-lite: 0.6.3 + source-map-js: 1.2.1 + webpack: 5.108.4(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack-cli@7.2.1) + + source-map-support@0.5.13: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map-support@0.5.19: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + source-map@0.7.4: {} + + source-map@0.7.6: {} + + spdy-transport@3.0.0: + dependencies: + debug: 4.4.3(supports-color@7.2.0) + detect-node: 2.1.0 + hpack.js: 2.1.6 + obuf: 1.1.2 + readable-stream: 3.6.2 + wbuf: 1.7.3 + transitivePeerDependencies: + - supports-color + + spdy@4.0.2: + dependencies: + debug: 4.4.3(supports-color@7.2.0) + handle-thing: 2.0.1 + http-deceiver: 1.2.7 + select-hose: 2.0.0 + spdy-transport: 3.0.0 + transitivePeerDependencies: + - supports-color + + sprintf-js@1.0.3: {} + + stack-utils@2.0.6: + dependencies: + escape-string-regexp: 2.0.0 + + statuses@1.5.0: {} + + statuses@2.0.2: {} + + streamsearch@1.1.0: {} + + string-argv@0.3.2: {} + + string-length@4.0.2: + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.2.0 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 + + string-width@8.2.2: + dependencies: + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + + strip-bom@3.0.0: {} + + strip-bom@4.0.0: {} + + strip-final-newline@2.0.0: {} + + strip-json-comments@3.1.1: {} + + strtok3@10.3.5: + dependencies: + '@tokenizer/token': 0.3.0 + + style-loader@3.3.4(webpack@5.108.4): + dependencies: + webpack: 5.108.4(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack-cli@7.2.1) + + stylehacks@7.0.11(postcss@8.5.18): + dependencies: + browserslist: 4.28.6 + postcss: 8.5.18 + postcss-selector-parser: 7.1.4 + + supabase@2.109.1: + dependencies: + eciesjs: 0.5.0 + jose: 6.2.3 + optionalDependencies: + '@supabase/cli-darwin-arm64': 2.109.1 + '@supabase/cli-darwin-x64': 2.109.1 + '@supabase/cli-linux-arm64': 2.109.1 + '@supabase/cli-linux-arm64-musl': 2.109.1 + '@supabase/cli-linux-x64': 2.109.1 + '@supabase/cli-linux-x64-musl': 2.109.1 + '@supabase/cli-windows-arm64': 2.109.1 + '@supabase/cli-windows-x64': 2.109.1 + + superagent@10.3.0: + dependencies: + component-emitter: 1.3.1 + cookiejar: 2.1.4 + debug: 4.4.3(supports-color@7.2.0) + fast-safe-stringify: 2.1.1 + form-data: 4.0.6 + formidable: 3.5.4 + methods: 1.1.2 + mime: 2.6.0 + qs: 6.15.3 + transitivePeerDependencies: + - supports-color + + supertest@7.2.2: + dependencies: + cookie-signature: 1.2.2 + methods: 1.1.2 + superagent: 10.3.0 + transitivePeerDependencies: + - supports-color + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + svgo@4.0.2: + dependencies: + commander: 11.1.0 + css-select: 5.2.2 + css-tree: 3.2.1 + css-what: 6.2.2 + csso: 5.0.5 + picocolors: 1.1.1 + sax: 1.6.0 + + sync-child-process@1.0.2: + dependencies: + sync-message-port: 1.2.0 + + sync-message-port@1.2.0: {} + + synckit@0.11.13: + dependencies: + '@pkgr/core': 0.3.6 + + tapable@2.3.3: {} + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.5 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + + tcp-port-used@1.0.3: + dependencies: + debug: 4.3.1 + is2: 2.0.1 + transitivePeerDependencies: + - supports-color + + terser-webpack-plugin@5.6.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack@5.108.4): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + jest-worker: 27.5.1 + schema-utils: 4.3.3 + terser: 5.49.0 + webpack: 5.108.4(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack-cli@7.2.1) + optionalDependencies: + '@swc/core': 1.15.43(@swc/helpers@0.5.23) + postcss: 8.5.18 + + terser@5.49.0: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.17.0 + commander: 2.20.3 + source-map-support: 0.5.21 + + test-exclude@6.0.0: + dependencies: + '@istanbuljs/schema': 0.1.6 + glob: 7.2.3 + minimatch: 3.1.5 + + thingies@2.6.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + + thunky@1.1.0: {} + + tinyexec@1.2.4: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + tmp@0.2.7: {} + + tmpl@1.0.5: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + token-types@6.1.2: + dependencies: + '@borewit/text-codec': 0.2.2 + '@tokenizer/token': 0.3.0 + ieee754: 1.2.1 + + tree-dump@1.1.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + + ts-api-utils@2.5.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.3.0)(jest@30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)))(typescript@5.9.3): + dependencies: + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + handlebars: 4.7.9 + jest: 30.3.0(@types/node@22.20.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3)) + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.8.5 + type-fest: 4.41.0 + typescript: 5.9.3 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.29.7 + '@jest/transform': 30.4.1 + '@jest/types': 30.4.1 + babel-jest: 30.4.1(@babel/core@7.29.7) + jest-util: 30.3.0 + + ts-loader@9.6.2(loader-utils@2.0.4)(typescript@5.9.3)(webpack@5.108.4): + dependencies: + chalk: 4.1.2 + picomatch: 4.0.5 + source-map: 0.7.6 + typescript: 5.9.3 + webpack: 5.108.4(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack-cli@7.2.1) + optionalDependencies: + loader-utils: 2.0.4 + + ts-morph@18.0.0: + dependencies: + '@ts-morph/common': 0.19.0 + code-block-writer: 12.0.0 + + ts-node@10.9.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(@types/node@22.20.1)(typescript@5.9.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.12 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 22.20.1 + acorn: 8.17.0 + acorn-walk: 8.3.5 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.4 + make-error: 1.3.6 + typescript: 5.9.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optionalDependencies: + '@swc/core': 1.15.43(@swc/helpers@0.5.23) + + ts-toolbelt@9.6.0: {} + + tsconfig-paths-webpack-plugin@4.2.0: + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.24.2 + tapable: 2.3.3 + tsconfig-paths: 4.2.0 + + tsconfig-paths@4.2.0: + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@1.14.1: {} + + tslib@2.8.1: {} + + tsyringe@4.10.0: + dependencies: + tslib: 1.14.1 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-detect@4.0.8: {} + + type-fest@0.21.3: {} + + type-fest@4.41.0: {} + + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + type-is@2.1.0: + dependencies: + content-type: 2.0.0 + media-typer: 1.1.0 + mime-types: 3.0.2 + + typed-assert@1.0.9: {} + + typedarray@0.0.6: {} + + types-ramda@0.31.0: + dependencies: + ts-toolbelt: 9.6.0 + + typescript-eslint@8.63.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.63.0(@typescript-eslint/parser@8.63.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/parser': 8.63.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.63.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.63.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.9.3) + eslint: 9.39.5(jiti@2.7.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + typescript@5.9.3: {} + + uglify-js@3.19.3: + optional: true + + uid@2.0.2: + dependencies: + '@lukeed/csprng': 1.1.0 + + uint8array-extras@1.5.0: {} + + undici-types@6.21.0: {} + + unicode-canonical-property-names-ecmascript@2.0.1: {} + + unicode-match-property-ecmascript@2.0.0: + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.1 + unicode-property-aliases-ecmascript: 2.2.0 + + unicode-match-property-value-ecmascript@2.2.1: {} + + unicode-property-aliases-ecmascript@2.2.0: {} + + union@0.5.0: + dependencies: + qs: 6.15.3 + + universalify@2.0.1: {} + + unpipe@1.0.0: {} + + unrs-resolver@1.12.2: + dependencies: + napi-postinstall: 0.3.4 + optionalDependencies: + '@unrs/resolver-binding-android-arm-eabi': 1.12.2 + '@unrs/resolver-binding-android-arm64': 1.12.2 + '@unrs/resolver-binding-darwin-arm64': 1.12.2 + '@unrs/resolver-binding-darwin-x64': 1.12.2 + '@unrs/resolver-binding-freebsd-x64': 1.12.2 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.12.2 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.12.2 + '@unrs/resolver-binding-linux-arm64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-arm64-musl': 1.12.2 + '@unrs/resolver-binding-linux-loong64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-loong64-musl': 1.12.2 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-riscv64-musl': 1.12.2 + '@unrs/resolver-binding-linux-s390x-gnu': 1.12.2 + '@unrs/resolver-binding-linux-x64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-x64-musl': 1.12.2 + '@unrs/resolver-binding-openharmony-arm64': 1.12.2 + '@unrs/resolver-binding-wasm32-wasi': 1.12.2 + '@unrs/resolver-binding-win32-arm64-msvc': 1.12.2 + '@unrs/resolver-binding-win32-ia32-msvc': 1.12.2 + '@unrs/resolver-binding-win32-x64-msvc': 1.12.2 + + update-browserslist-db@1.2.3(browserslist@4.28.6): + dependencies: + browserslist: 4.28.6 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + url-join@4.0.1: {} + + util-deprecate@1.0.2: {} + + utils-merge@1.0.1: {} + + uuid@8.3.2: {} + + v8-compile-cache-lib@3.0.1: {} + + v8-to-istanbul@9.3.0: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 + + validator@13.15.35: {} + + varint@6.0.0: {} + + vary@1.1.2: {} + + walker@1.0.8: + dependencies: + makeerror: 1.0.12 + + watchpack@2.5.2: + dependencies: + graceful-fs: 4.2.11 + + wbuf@1.7.3: + dependencies: + minimalistic-assert: 1.0.1 + + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + + webpack-cli@7.2.1(js-yaml@4.3.0)(json5@2.2.3)(webpack-dev-server@5.2.6)(webpack@5.108.4): + dependencies: + '@discoveryjs/json-ext': 1.1.0 + commander: 14.0.3 + cross-spawn: 7.0.6 + envinfo: 7.21.0 + import-local: 3.2.0 + interpret: 3.1.1 + rechoir: 0.8.0 + webpack: 5.108.4(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack-cli@7.2.1) + webpack-merge: 6.0.1 + optionalDependencies: + js-yaml: 4.3.0 + json5: 2.2.3 + webpack-dev-server: 5.2.6(tslib@2.8.1)(webpack-cli@7.2.1)(webpack@5.108.4) + + webpack-dev-middleware@7.4.5(tslib@2.8.1)(webpack@5.108.4): + dependencies: + colorette: 2.0.20 + memfs: 4.64.0(tslib@2.8.1) + mime-types: 3.0.2 + on-finished: 2.4.1 + range-parser: 1.3.0 + schema-utils: 4.3.3 + optionalDependencies: + webpack: 5.108.4(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack-cli@7.2.1) + transitivePeerDependencies: + - tslib + + webpack-dev-server@5.2.6(tslib@2.8.1)(webpack-cli@7.2.1)(webpack@5.108.4): + dependencies: + '@types/bonjour': 3.5.13 + '@types/connect-history-api-fallback': 1.5.4 + '@types/express': 4.17.25 + '@types/express-serve-static-core': 4.19.9 + '@types/serve-index': 1.9.4 + '@types/serve-static': 1.15.10 + '@types/sockjs': 0.3.36 + '@types/ws': 8.18.1 + ansi-html-community: 0.0.8 + bonjour-service: 1.4.3 + chokidar: 3.6.0 + colorette: 2.0.20 + compression: 1.8.1 + connect-history-api-fallback: 2.0.0 + express: 4.22.2 + graceful-fs: 4.2.11 + http-proxy-middleware: 2.0.10(@types/express@4.17.25) + ipaddr.js: 2.4.0 + launch-editor: 2.14.1 + open: 10.2.0 + p-retry: 6.2.1 + schema-utils: 4.3.3 + selfsigned: 5.5.0 + serve-index: 1.9.2 + sockjs: 0.3.24 + spdy: 4.0.2 + webpack-dev-middleware: 7.4.5(tslib@2.8.1)(webpack@5.108.4) + ws: 8.21.0 + optionalDependencies: + webpack: 5.108.4(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack-cli@7.2.1) + webpack-cli: 7.2.1(js-yaml@4.3.0)(json5@2.2.3)(webpack-dev-server@5.2.6)(webpack@5.108.4) + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - tslib + - utf-8-validate + + webpack-merge@6.0.1: + dependencies: + clone-deep: 4.0.1 + flat: 5.0.2 + wildcard: 2.0.1 + + webpack-node-externals@3.0.0: {} + + webpack-sources@3.5.1: {} + + webpack-subresource-integrity@5.1.0(webpack@5.108.4): + dependencies: + typed-assert: 1.0.9 + webpack: 5.108.4(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack-cli@7.2.1) + + webpack@5.108.4(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack-cli@7.2.1): + dependencies: + '@types/estree': 1.0.9 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.17.0 + acorn-import-phases: 1.0.4(acorn@8.17.0) + browserslist: 4.28.6 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.24.2 + es-module-lexer: 2.3.1 + eslint-scope: 5.1.1 + events: 3.3.0 + graceful-fs: 4.2.11 + loader-runner: 4.3.2 + mime-db: 1.54.0 + minimizer-webpack-plugin: 5.6.1(@swc/core@1.15.43(@swc/helpers@0.5.23))(postcss@8.5.18)(webpack@5.108.4) + neo-async: 2.6.2 + schema-utils: 4.3.3 + tapable: 2.3.3 + watchpack: 2.5.2 + webpack-sources: 3.5.1 + optionalDependencies: + webpack-cli: 7.2.1(js-yaml@4.3.0)(json5@2.2.3)(webpack-dev-server@5.2.6)(webpack@5.108.4) + transitivePeerDependencies: + - '@minify-html/node' + - '@swc/core' + - '@swc/css' + - '@swc/html' + - clean-css + - cssnano + - csso + - esbuild + - html-minifier-terser + - lightningcss + - postcss + - uglify-js + + websocket-driver@0.7.5: + dependencies: + http-parser-js: 0.5.10 + safe-buffer: 5.2.1 + websocket-extensions: 0.1.4 + + websocket-extensions@0.1.4: {} + + whatwg-encoding@2.0.0: + dependencies: + iconv-lite: 0.6.3 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + which@3.0.1: + dependencies: + isexe: 2.0.0 + + wildcard@2.0.1: {} + + word-wrap@1.2.5: {} + + wordwrap@1.0.0: {} + + wrap-ansi@10.0.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 8.2.2 + strip-ansi: 7.2.0 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.2.0 + + wrap-ansi@9.0.2: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.2.0 + + wrappy@1.0.2: {} + + write-file-atomic@5.0.1: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 4.1.0 + + ws@8.21.0: {} + + wsl-utils@0.1.0: + dependencies: + is-wsl: 3.1.1 + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yaml@1.10.3: {} + + yaml@2.9.0: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yargs@17.7.3: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yn@3.1.1: {} + + yocto-queue@0.1.0: {} + + yocto-queue@1.2.2: {} + + zod@3.25.76: {} + + zod@4.4.3: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..4bbdcbf --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,11 @@ +packages: + - 'apps/*' + - 'libs/*' + +autoInstallPeers: true +allowBuilds: + '@parcel/watcher': true + '@swc/core': true + less: true + nx: true + unrs-resolver: true diff --git a/supabase/config.toml b/supabase/config.toml index 3ebfb93..97c05ed 100644 --- a/supabase/config.toml +++ b/supabase/config.toml @@ -1,3 +1,5 @@ +# 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 = "nxt-backend" @@ -7,22 +9,37 @@ enabled = true # Port to use for the API URL. port = 54321 # Schemas to expose in your API. Tables, views and stored procedures in this schema will get API -# endpoints. `public` is always included. +# 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. `public` is always included. +# 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 +# Controls whether new tables, views, sequences and functions created in the `public` schema by +# `postgres` are reachable through the Data API roles (`anon`, `authenticated`, `service_role`) +# without explicit GRANTs. When unset, new entities are NOT auto-exposed, matching the new cloud +# default. Set to `true` to keep the legacy behaviour of auto-exposing new entities; this is +# deprecated and the field is removed on 2026-10-30 once the always-revoked behaviour is permanent. +# auto_expose_new_tables = true + +[api.tls] +# Enable HTTPS endpoints locally using a self-signed certificate. +enabled = false +# Paths to self-signed certificate pair. +# cert_path = "../certs/my-cert.pem" +# key_path = "../certs/my-key.pem" [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 +# Maximum amount of time to wait for health check when starting the local database. +health_timeout = "2m" # 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 +major_version = 17 [db.pooler] enabled = false @@ -36,6 +53,37 @@ default_pool_size = 20 # Maximum number of client connections allowed. max_client_conn = 100 +# [db.vault] +# secret_key = "env(SECRET_VALUE)" + +[db.migrations] +# If disabled, migrations will be skipped during a db push or reset. +enabled = true +# Specifies an ordered list of schema files, directories, or glob patterns that describe your database. +# Supports paths relative to supabase directory: "./schemas/*.sql", "./database". +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.sql"] + +[db.network_restrictions] +# Enable management of network restrictions. +enabled = false +# List of IPv4 CIDR blocks allowed to connect to the database. +# Defaults to allow all IPv4 connections. Set empty array to block all IPs. +allowed_cidrs = ["0.0.0.0/0"] +# List of IPv6 CIDR blocks allowed to connect to the database. +# Defaults to allow all IPv6 connections. Set empty array to block all IPs. +allowed_cidrs_v6 = ["::/0"] + +# Uncomment to reject non-secure connections to the database. +# [db.ssl_enforcement] +# enabled = true + [realtime] enabled = true # Bind realtime via either IPv4 or IPv6. (default: IPv4) @@ -54,31 +102,71 @@ 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] +[local_smtp] enabled = true # Port to use for the email testing server web interface. port = 54324 # Uncomment to expose additional ports for testing user applications that send emails. # smtp_port = 54325 # pop3_port = 54326 +# admin_email = "admin@email.com" +# sender_name = "Admin" [storage] enabled = true # The maximum file size allowed (e.g. "5MB", "500KB"). file_size_limit = "50MiB" -[storage.image_transformation] +# 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" + +# Allow connections via S3 compatible clients +[storage.s3_protocol] enabled = true +# Image transformation API is available to Supabase Pro plan. +# [storage.image_transformation] +# enabled = true + +# Store analytical data in S3 for running ETL jobs over Iceberg Catalog +# This feature is only available on the hosted platform. +[storage.analytics] +enabled = false +max_namespaces = 5 +max_tables = 10 +max_catalogs = 2 + +# Analytics Buckets is available to Supabase Pro plan. +# [storage.analytics.buckets.my-warehouse] + +# Store vector embeddings in S3 for large and durable datasets +[storage.vector] +enabled = true +max_buckets = 10 +max_indexes = 5 + +# Vector Buckets is available to Supabase Pro plan. +# [storage.vector.buckets.documents-openai] + [auth] enabled = true # The base URL of your website. Used as an allow-list for redirects and for constructing URLs used # in emails. site_url = "http://127.0.0.1:3000" +# The public URL that Auth serves on. Defaults to the API external URL with `/auth/v1` appended. +# external_url = "" # A list of *exact* URLs that auth providers are permitted to redirect to post authentication. additional_redirect_urls = ["https://127.0.0.1:3000"] # How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 (1 week). jwt_expiry = 3600 +# JWT issuer URL. If not set, defaults to auth.external_url. +# jwt_issuer = "" +# Path to JWT signing key. DO NOT commit your signing keys file to git. +# signing_keys_path = "./signing_keys.json" # 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. @@ -90,6 +178,43 @@ enable_signup = true 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 = "" + +# Configure passkey sign-ins. +# [auth.passkey] +# enabled = false + +# Configure WebAuthn relying party settings (required when passkey is enabled). +# [auth.webauthn] +# rp_display_name = "Supabase" +# rp_id = "localhost" +# rp_origins = ["http://127.0.0.1:3000"] + +[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 +# Number of Web3 logins that can be made in a 5 minute interval per IP address. +web3 = 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. @@ -99,21 +224,43 @@ 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 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" +# Uncomment to customize notification email template +# [auth.email.notification.password_changed] +# enabled = true +# subject = "Your password has been changed" +# content_path = "./templates/password_changed_notification.html" + [auth.sms] # Allow/disallow new user signups via SMS to your project. -enable_signup = true +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 }} ." +template = "Your code is {{ `{{ .Code }}` }}" # Controls the minimum amount of time that must pass before sending another sms otp. max_frequency = "5s" @@ -121,6 +268,18 @@ max_frequency = "5s" # [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 new user is created and allows developers to reject the request based on the incoming user object. +# [auth.hook.before_user_created] +# enabled = true +# uri = "pg-functions://postgres/auth/before-user-created-hook" + # 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 @@ -134,33 +293,105 @@ 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.google] +# `twitter`, `x`, `slack`, `spotify`, `workos`, `zoom`. +[auth.external.apple] enabled = false -client_id = "env(SUPABASE_AUTH_EXTERNAL_GOOGLE_CLIENT_ID)" +client_id = "" # DO NOT commit your OAuth provider secret to git. Use environment variable substitution instead: -secret = "env(SUPABASE_AUTH_EXTERNAL_GOOGLE_SECRET)" -# Overrides the default auth redirectUrl. +secret = "env(SUPABASE_AUTH_EXTERNAL_APPLE_SECRET)" +# Overrides the default auth callback URL derived from auth.external_url. 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 +# If enabled, it will allow the user to successfully authenticate when the provider does not return an email address. +email_optional = false + +# Allow Solana wallet holders to sign in to your project via the Sign in with Solana (SIWS, EIP-4361) standard. +# You can configure "web3" rate limit in the [auth.rate_limit] section and set up [auth.captcha] if self-hosting. +[auth.web3.solana] +enabled = 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" + +# OAuth server configuration +[auth.oauth_server] +# Enable OAuth server functionality +enabled = false +# Path for OAuth consent flow UI +authorization_url_path = "/oauth/consent" +# Allow dynamic client registration +allow_dynamic_registration = false [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" +# Supported request policies: `oneshot`, `per_worker`. +# `per_worker` (default) — enables hot reload during local development. +# `oneshot` — fallback mode if hot reload causes issues (e.g. in large repos or with symlinks). +policy = "per_worker" +# Port to attach the Chrome inspector for debugging edge functions. inspector_port = 8083 +# The Deno major version to use. +deno_version = 2 + +# [edge_runtime.secrets] +# secret_key = "env(SECRET_VALUE)" [analytics] -enabled = true +# Local dev only: Logflare + vector often miss the 2m health-check window on slower +# machines or right after a cold image pull. OSS schema work does not need analytics +# locally; legacy/supabase/config.toml uses the same setting. Re-enable if you need +# Studio log browsing against a local Logflare instance. +enabled = false port = 54327 -vector_port = 54328 # Configure one of the supported backends: `postgres`, `bigquery`. backend = "postgres" @@ -177,13 +408,11 @@ s3_access_key = "env(S3_ACCESS_KEY)" # Configures AWS_SECRET_ACCESS_KEY for S3 bucket s3_secret_key = "env(S3_SECRET_KEY)" -[functions.meter-consumption-2] +# pg-delta is the schema diff engine for db diff / db pull / db remote commit. +# Set enabled = false to fall back to the legacy migra engine. +[experimental.pgdelta] enabled = true -verify_jwt = true -import_map = "./functions/meter-consumption-2/deno.json" -# Uncomment to specify a custom file path to the entrypoint. -# Supported file extensions are: .ts, .js, .mjs, .jsx, .tsx -entrypoint = "./functions/meter-consumption-2/index.ts" -# Specifies static files to be bundled with the function. Supports glob patterns. -# For example, if you want to serve static HTML pages in your function: -# static_files = [ "./functions/meter-consumption-2/*.html" ] +# Directory under `supabase/` where declarative files are written. +# declarative_schema_path = "./database" +# JSON string passed through to pg-delta SQL formatting. +# format_options = "{\"keywordCase\":\"upper\",\"indent\":2,\"maxWidth\":80,\"commaStyle\":\"trailing\"}" diff --git a/supabase/functions/_shared/auth.ts b/supabase/functions/_shared/auth.ts deleted file mode 100644 index 7164eac..0000000 --- a/supabase/functions/_shared/auth.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { createClient } from 'https://esm.sh/@supabase/supabase-js'; - -export async function getContextForOdissey(req) { - const supabaseAdmin = createClient( - Deno.env.get('SUPABASE_URL') ?? '', - Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? '', - ); - - const bearerToken = req.headers.get('Authorization'); - const token = bearerToken?.replace('Bearer ', '') || ''; - - let mockAccount: any = null; - if (token) { - // Look up API key in the database - const { data: apiKeyData, error } = await supabaseAdmin - .from('api_keys') - .select(` - id, - key, - is_locked, - account_id, - accounts!inner( - id, - full_name, - email, - supabase_id, - organization_id, - organizations!inner( - id, - name, - organization_type - ) - ) - `) - .eq('key', token) - .eq('is_locked', false) - .single(); - - // We need to generate a mock user, since the supabase client does not allow - // to mint sessions on behalf of a user - if (apiKeyData && !error) mockAccount = { - id: apiKeyData.accounts.id, - email: apiKeyData.accounts.email, - app_metadata: { - organization_id: apiKeyData.accounts.organization_id, - organization_type: apiKeyData.accounts.organizations.organization_type, - }, - // Optionally include other fields as needed - } - } - - return { supabase: null, user: mockAccount }; -} - -export async function getContext(req) { - const bearerToken = req.headers.get('Authorization'); - const token = bearerToken.replace('Bearer ', ''); - - const supabase = createClient( - Deno.env.get('SUPABASE_URL') ?? '', - Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? '', - { global: { headers: { Authorization: bearerToken! } } }, - ); - - // Try to get user from JWT token first - const { data: { user } } = await supabase.auth.getUser(token); - - return { supabase, user }; -} - -export function canAccessLenderApi(user) { - const { app_metadata } = user; - - // If the user belongs to NXT Grid, the allow access - if(2 === app_metadata.organization_id) return true; - - // If the user is a member of a lender organization - if('LENDER' === app_metadata.organization_type) return true; - - return false; -} - -export function canAccessDataAggregatorApi(user) { - const { app_metadata } = user; - - // If the user belongs to NXT Grid, the allow access - if(2 === app_metadata.organization_id) return true; - - // If the user is a member of a data aggregator organization - if('DATA_AGGREGATOR' === app_metadata.organization_type) return true; - - return false; -} diff --git a/supabase/functions/_shared/cors.ts b/supabase/functions/_shared/cors.ts deleted file mode 100644 index 14d9864..0000000 --- a/supabase/functions/_shared/cors.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const corsHeaders = { - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', -}; diff --git a/supabase/functions/customers/index.ts b/supabase/functions/customers/index.ts deleted file mode 100644 index bface31..0000000 --- a/supabase/functions/customers/index.ts +++ /dev/null @@ -1,92 +0,0 @@ -// Follow this setup guide to integrate the Deno language server with your editor: -// https://deno.land/manual/getting_started/setup_your_environment -// This enables autocomplete, go to definition, etc. - -// Setup type definitions for built-in Supabase Runtime APIs -import 'jsr:@supabase/functions-js/edge-runtime.d.ts'; -import { canAccessLenderApi, getContext } from '../_shared/auth.ts'; -import { corsHeaders } from '../_shared/cors.ts'; - -async function getTask(req, supabase, user) { - const isAuthorized = canAccessLenderApi(user); - - if(!isAuthorized) return new Response(JSON.stringify({ error: 'Not authorized' }), { - headers: { 'Content-Type': 'application/json' }, - status: 401, - }); - - const url = new URL(req.url); - - const page = Number(url.searchParams.get('page') || 1); - const size = Number(url.searchParams.get('size') || 100); - const order = url.searchParams.get('order') || 'id'; - const ascending = url.searchParams.get('ascending') || 'true'; - - const from = (page - 1) * size; - const to = from + size - 1; - - const { data, count: total, error } = await supabase - .from('customers') - .select('*, accounts!inner(full_name, deleted_at), grids!inner(name, is_hidden_from_reporting)', { count: 'exact' }) - .eq('is_hidden_from_reporting', false) - .is('grids.deleted_at', null) - .is('grids.is_hidden_from_reporting', false) - .order(order, { ascending: ascending === 'true' }) - .range(from, to); - - console.info(data); - if(error) throw Error('An error occurred while trying to read the data'); - - const entries = data.map(entry => ({ - id: entry.id, - full_name: '*******', - grid: entry.grids.name, - })); - - return new Response( - JSON.stringify({ - total, - page, - size, - total_pages: Math.ceil(total / size), - entries, - }), - { headers: { - ...corsHeaders, - 'Content-Type': 'application/json', - } }, - ); -} - -Deno.serve(async req => { - const { method } = req; - - // TODO: I do not like the idea of returning responses from functions - // being called here. Rather, a result should be called, and a response - // built in this very function with that result - switch (true) { - case method === 'GET': - // eslint-disable-next-line no-case-declarations - const { supabase, user } = await getContext(req); - return getTask(req, supabase, user); - case method === 'OPTIONS': - return new Response('ok', { headers: corsHeaders }); - default: - return new Response(JSON.stringify({ error: 'Method not supported' }), { - headers: { 'Content-Type': 'application/json' }, - status: 400, - }); - } -}); - -/* To invoke locally: - - 1. Run `supabase start` (see: https://supabase.com/docs/reference/cli/supabase-start) - 2. Make an HTTP request: - - curl -i --location --request POST 'http://127.0.0.1:54321/functions/v1/customers' \ - --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0' \ - --header 'Content-Type: application/json' \ - --data '{"name":"Functions"}' - -*/ diff --git a/supabase/functions/meter-consumption-2/.npmrc b/supabase/functions/meter-consumption-2/.npmrc deleted file mode 100644 index 48c6388..0000000 --- a/supabase/functions/meter-consumption-2/.npmrc +++ /dev/null @@ -1,3 +0,0 @@ -# Configuration for private npm package dependencies -# For more information on using private registries with Edge Functions, see: -# https://supabase.com/docs/guides/functions/import-maps#importing-from-private-registries diff --git a/supabase/functions/meter-consumption-2/deno.json b/supabase/functions/meter-consumption-2/deno.json deleted file mode 100644 index f6ca845..0000000 --- a/supabase/functions/meter-consumption-2/deno.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "imports": {} -} diff --git a/supabase/functions/meter-consumption-2/index.ts b/supabase/functions/meter-consumption-2/index.ts deleted file mode 100644 index 5f6ae5c..0000000 --- a/supabase/functions/meter-consumption-2/index.ts +++ /dev/null @@ -1,193 +0,0 @@ -// Follow this setup guide to integrate the Deno language server with your editor: -// https://deno.land/manual/getting_started/setup_your_environment -// This enables autocomplete, go to definition, etc. - -// Setup type definitions for built-in Supabase Runtime APIs -import 'jsr:@supabase/functions-js/edge-runtime.d.ts'; -import { canAccessDataAggregatorApi, getContextForOdissey } from '../_shared/auth.ts'; -import { corsHeaders } from '../_shared/cors.ts'; -import { Client } from 'https://deno.land/x/postgres@v0.17.0/mod.ts'; -interface TimescaleConfig { - hostname: string; - port: number; - database: string; - user: string; - password: string; -} - -async function getTimescaleConfig(): Promise { - return { - hostname: Deno.env.get('PHOENIX_HOST') ?? '', - port: parseInt(Deno.env.get('PHOENIX_PORT') ?? '5432'), - database: Deno.env.get('PHOENIX_DATABASE') ?? '', - user: Deno.env.get('PHOENIX_USERNAME') ?? '', - password: Deno.env.get('PHOENIX_PASSWORD') ?? '', - }; -} - -async function getMeterConsumption(req) { - const url = new URL(req.url); - const offset = parseInt(url.searchParams.get('offset') || '0'); - const pageLimit = parseInt(url.searchParams.get('pageLimit') || '20'); - const fromDate = url.searchParams.get('FROM'); - const toDate = url.searchParams.get('TO'); - const site = url.searchParams.get('site'); - - if (!fromDate || !toDate) { - return new Response(JSON.stringify({ - error: 'Missing required parameters: FROM, TO' - }), { - headers: { - ...corsHeaders, - 'Content-Type': 'application/json' - }, - status: 400, - }); - } - - let client; - try { - const config = await getTimescaleConfig(); - client = new Client(config); - - await client.connect(); - - // First query: Get total count - const params = [fromDate, toDate]; - let countQuery = ` - SELECT COUNT(*) as total_count - FROM meter_snapshot_1_h - WHERE created_at >= $1 - AND created_at <= $2 - AND is_hidden_from_reporting = false - AND consumption_kwh IS NOT NULL - AND NOT (consumption_kwh::text = 'NaN') - `; - - if (site) { - countQuery += ` AND LOWER(grid_name) = LOWER($3)`; - params.push(site) - } - - const countResult = await client.queryObject(countQuery, params); - const total = parseInt(countResult.rows[0].total_count); - - // Second query: Get paginated data - const dataParams: any[] = [fromDate, toDate]; - let i = 1; - let dataQuery = ` - SELECT - created_at as timestamp, - meter_external_reference as "meterId", - COALESCE(consumption_kwh::numeric, 0) as "energyConsumptionKwh", - 60 as "timeIntervalMinutes", - customer_id as "customerAccountId" - FROM meter_snapshot_1_h - WHERE created_at >= $${i++} - AND created_at <= $${i++} - AND is_hidden_from_reporting = false - AND consumption_kwh IS NOT NULL - AND NOT (consumption_kwh::text = 'NaN')`; - - - if (site) { - dataQuery += ` AND LOWER(grid_name) = LOWER($${i++})` - dataParams.push(site) - } - - dataQuery += ` ORDER BY created_at LIMIT $${i++} OFFSET $${i++}` - dataParams.push(pageLimit, offset) - - const result = await client.queryObject(dataQuery, dataParams); - const readings = result.rows.map(row => ({ - ...row, - energyConsumptionKwh: Math.round(parseFloat(row.energyConsumptionKwh) * 1000) / 1000 - })); - - - - return new Response( - JSON.stringify({ - total: total, - offset: offset, - pageLimit: pageLimit, - readings: readings, - }), - { - headers: { - ...corsHeaders, - 'Content-Type': 'application/json', - } - } - ); - - } catch (error) { - console.error('Error querying TimescaleDB:', error); - return new Response(JSON.stringify({ - error: 'Failed to retrieve meter consumption data', - details: error.message - }), { - headers: { - ...corsHeaders, - 'Content-Type': 'application/json' - }, - status: 500, - }); - } finally { - await client.end(); - } -} - -Deno.serve(async req => { - const { method } = req; - - const { supabase, user } = await getContextForOdissey(req); - - if(!user) { - return new Response(JSON.stringify({ error: 'Authentication required' }), { - headers: { - ...corsHeaders, - 'Content-Type': 'application/json' - }, - status: 401, - }); - } - - const isAuthorized = canAccessDataAggregatorApi(user); - - if (!isAuthorized) { - return new Response(JSON.stringify({ error: 'Not authorized' }), { - headers: { - ...corsHeaders, - 'Content-Type': 'application/json' - }, - status: 401, - }); - } - - switch (true) { - case method === 'GET': - return getMeterConsumption(req); - case method === 'OPTIONS': - return new Response('ok', { headers: corsHeaders }); - default: - return new Response(JSON.stringify({ error: 'Method not supported' }), { - headers: { - ...corsHeaders, - 'Content-Type': 'application/json' - }, - status: 400, - }); - } -}); - -/* To invoke locally: - - 1. Run `supabase start` (see: https://supabase.com/docs/reference/cli/supabase-start) - 2. Make an HTTP request: - - curl -i --location --request GET 'http://127.0.0.1:54321/functions/v1/meter-consumption?offset=0&pageLimit=20&FROM=2025-07-11T00:00:00.000Z&TO=2025-07-11T23:59:59.999Z' \ - --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0' \ - --header 'Content-Type: application/json' - -*/ diff --git a/supabase/functions/meter-consumption/README.md b/supabase/functions/meter-consumption/README.md deleted file mode 100644 index 6b93be8..0000000 --- a/supabase/functions/meter-consumption/README.md +++ /dev/null @@ -1,183 +0,0 @@ -# Meter Consumption Edge Function - -This Supabase Edge Function retrieves meter consumption data from a TimescaleDB instance. - -## Setup - -### 1. Configure TimescaleDB Secrets - -Set the following secrets in your Supabase project: - -```bash -# Set Phoenix (TimescaleDB) connection parameters -supabase secrets set PHOENIX_HOST=your-phoenix-host -supabase secrets set PHOENIX_PORT=5432 -supabase secrets set PHOENIX_DATABASE=your-database-name -supabase secrets set PHOENIX_USERNAME=your-username -supabase secrets set PHOENIX_PASSWORD=your-password -``` - -### 2. Deploy the Function - -```bash -supabase functions deploy meter-consumption -``` - -## Usage - -### API Endpoint - -``` -GET /functions/v1/meter-consumption -``` - -### Required Parameters - -- `FROM`: Start date for the consumption data (ISO format: YYYY-MM-DDTHH:mm:ss.sssZ) -- `TO`: End date for the consumption data (ISO format: YYYY-MM-DDTHH:mm:ss.sssZ) - -### Optional Parameters - -- `offset`: Number of records to skip (default: 0) -- `pageLimit`: Number of records to return per page (default: 20) - -### Example Request - -**Using JWT Token:** -```bash -curl -i --location --request GET 'https://your-project.supabase.co/functions/v1/meter-consumption?offset=0&pageLimit=20&FROM=2025-07-11T00:00:00.000Z&TO=2025-07-11T23:59:59.999Z' \ - --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \ - --header 'Content-Type: application/json' -``` - -**Using API Key:** -```bash -curl -i --location --request GET 'https://your-project.supabase.co/functions/v1/meter-consumption?offset=0&pageLimit=20&FROM=2025-07-11T00:00:00.000Z&TO=2025-07-11T23:59:59.999Z' \ - --header 'X-API-Key: YOUR_API_KEY' \ - --header 'Content-Type: application/json' -``` - -### Response Format - -```json -{ - "total": 72, - "offset": 0, - "pageLimit": 20, - "readings": [ - { - "timestamp": "2025-07-11T00:00:00.000Z", - "meterId": "METER_001", - "energyConsumptionKwh": 15.23, - "timeIntervalMinutes": 60, - "customerAccountId": "N/A" - }, - { - "timestamp": "2025-07-11T00:00:00.000Z", - "meterId": "METER_002", - "energyConsumptionKwh": 8.45, - "timeIntervalMinutes": 60, - "customerAccountId": "N/A" - }, - { - "timestamp": "2025-07-11T01:00:00.000Z", - "meterId": "METER_001", - "energyConsumptionKwh": 0, - "timeIntervalMinutes": 60, - "customerAccountId": "N/A" - } - ] -} -``` - -## TimescaleDB Features Used - -This function leverages TimescaleDB's powerful time-series capabilities: - -- **`time_bucket('1 hour', created_at)`**: Groups data into 1-hour buckets, automatically handling time zone and alignment -- **`generate_series()`**: Creates a complete time series for the requested date range -- **`LEFT JOIN`**: Ensures all hours in the range are included, even those with no data -- **`COALESCE()`**: Returns zero values for hours with no meter readings - -This approach ensures you get a complete hourly time series with no gaps, making it perfect for time-series analysis and visualization. - -## Database Schema - -The function queries the `meter_snapshot_1_h` table which has the following structure: - -```sql -CREATE TABLE meter_snapshot_1_h ( - created_at timestamp NOT NULL, - meter_id int NOT NULL, - meter_type varchar, - meter_external_reference varchar, - meter_external_system varchar, - meter_phase varchar, - customer_full_name varchar, - customer_id int, - dcu_id int, - dcu_external_reference varchar, - dcu_external_system varchar, - grid_id int, - organization_id int, - grid_name varchar, - organization_name varchar, - consumption_kwh float8, - connection_id int, - power_limit int, - power_limit_kw float8, - power_limit_kw_should_be float8, - is_on boolean, - should_be_on boolean, - is_fs_consumption boolean, - is_hps_consumption boolean, - is_hidden_from_reporting boolean NOT NULL DEFAULT false, - is_cabin_meter boolean NOT NULL DEFAULT false, - kwh_credit_available float4, - PRIMARY KEY (created_at, meter_id) -); -``` - -**Key mappings for the API response:** -- `time_bucket('1 hour', created_at)` → `timestamp` (grouped by hourly buckets using TimescaleDB's time_bucket function) -- `meter_external_reference` → `meterId` (individual meter external reference for each meter-hour combination) -- `SUM(consumption_kwh)` → `energyConsumptionKwh` (consumption for each specific meter in each hour) -- `customerAccountId` is set to "N/A" for grouped results -- `timeIntervalMinutes` is hardcoded to 60 (1 hour snapshots) -- Hours with no data for a specific meter return zero values (0.0) for energy consumption -- The query groups by both meter and hour, ensuring each meter-hour combination is represented - -## Authentication - -This function supports two authentication methods: - -### 1. JWT Token Authentication -- Standard Supabase JWT token in the `Authorization: Bearer ` header -- User must be authenticated through Supabase Auth - -### 2. API Key Authentication -- API key in the `X-API-Key` header -- API key must exist in the `api_keys` table and not be locked (`is_locked = false`) -- API key must be associated with a valid account and organization - -## Authorization - -This function uses specific authorization logic for data aggregator access: -- Users must belong to NXT Grid (organization_id = 2), OR -- Users must be members of a DATA_AGGREGATOR organization - -The same authorization rules apply whether using JWT tokens or API keys. - -## Error Handling - -The function includes comprehensive error handling for: -- Missing required parameters -- Database connection failures -- Query execution errors -- Authorization failures - ---- - -## License - -This project is licensed under the [Mozilla Public License 2.0](https://www.mozilla.org/MPL/2.0/). See the [`LICENSE`](../../../LICENSE) file at the repository root. diff --git a/supabase/functions/meter-consumption/index.ts b/supabase/functions/meter-consumption/index.ts deleted file mode 100644 index e01e6de..0000000 --- a/supabase/functions/meter-consumption/index.ts +++ /dev/null @@ -1,178 +0,0 @@ -// Follow this setup guide to integrate the Deno language server with your editor: -// https://deno.land/manual/getting_started/setup_your_environment -// This enables autocomplete, go to definition, etc. - -// Setup type definitions for built-in Supabase Runtime APIs -import 'jsr:@supabase/functions-js/edge-runtime.d.ts'; -import { canAccessDataAggregatorApi, getContextForOdissey } from '../_shared/auth.ts'; -import { corsHeaders } from '../_shared/cors.ts'; -import { Client } from 'https://deno.land/x/postgres@v0.17.0/mod.ts'; -interface TimescaleConfig { - hostname: string; - port: number; - database: string; - user: string; - password: string; -} - -async function getTimescaleConfig(): Promise { - return { - hostname: Deno.env.get('PHOENIX_HOST') ?? '', - port: parseInt(Deno.env.get('PHOENIX_PORT') ?? '5432'), - database: Deno.env.get('PHOENIX_DATABASE') ?? '', - user: Deno.env.get('PHOENIX_USERNAME') ?? '', - password: Deno.env.get('PHOENIX_PASSWORD') ?? '', - }; -} - -async function getMeterConsumption(req) { - const url = new URL(req.url); - const offset = parseInt(url.searchParams.get('offset') || '0'); - const pageLimit = parseInt(url.searchParams.get('pageLimit') || '20'); - const fromDate = url.searchParams.get('FROM'); - const toDate = url.searchParams.get('TO'); - // const site = url.searchParams.get('site'); - // If we have okp1 or okp2, then limit the search to okp - - if (!fromDate || !toDate) { - return new Response(JSON.stringify({ - error: 'Missing required parameters: FROM, TO' - }), { - headers: { - ...corsHeaders, - 'Content-Type': 'application/json' - }, - status: 400, - }); - } - - try { - const config = await getTimescaleConfig(); - console.info(config) - const client = new Client(config); - - await client.connect(); - - // First query: Get total count - const countQuery = ` - SELECT COUNT(*) as total_count - FROM meter_snapshot_1_h - WHERE created_at >= $1 - AND created_at <= $2 - AND is_hidden_from_reporting = false - AND consumption_kwh IS NOT NULL - AND NOT (consumption_kwh::text = 'NaN') - `; - - const countResult = await client.queryObject(countQuery, [fromDate, toDate]); - const total = parseInt(countResult.rows[0].total_count); - - // Second query: Get paginated data - const dataQuery = ` - SELECT - created_at as timestamp, - meter_external_reference as "meterId", - COALESCE(consumption_kwh::numeric, 0) as "energyConsumptionKwh", - 60 as "timeIntervalMinutes", - customer_id as "customerAccountId" - FROM meter_snapshot_1_h - WHERE created_at >= $1 - AND created_at <= $2 - AND is_hidden_from_reporting = false - AND consumption_kwh IS NOT NULL - AND NOT (consumption_kwh::text = 'NaN') - ORDER BY created_at - LIMIT $3 OFFSET $4 - `; - - const result = await client.queryObject(dataQuery, [fromDate, toDate, pageLimit, offset]); - const readings = result.rows.map(row => ({ - ...row, - energyConsumptionKwh: Math.round(parseFloat(row.energyConsumptionKwh) * 1000) / 1000 - })); - - await client.end(); - - return new Response( - JSON.stringify({ - total: total, - offset: offset, - pageLimit: pageLimit, - readings: readings, - }), - { - headers: { - ...corsHeaders, - 'Content-Type': 'application/json', - } - } - ); - - } catch (error) { - console.error('Error querying TimescaleDB:', error); - return new Response(JSON.stringify({ - error: 'Failed to retrieve meter consumption data', - details: error.message - }), { - headers: { - ...corsHeaders, - 'Content-Type': 'application/json' - }, - status: 500, - }); - } -} - -Deno.serve(async req => { - const { method } = req; - - const { supabase, user } = await getContextForOdissey(req); - - if(!user) { - return new Response(JSON.stringify({ error: 'Authentication required' }), { - headers: { - ...corsHeaders, - 'Content-Type': 'application/json' - }, - status: 401, - }); - } - - const isAuthorized = canAccessDataAggregatorApi(user); - - if (!isAuthorized) { - return new Response(JSON.stringify({ error: 'Not authorized' }), { - headers: { - ...corsHeaders, - 'Content-Type': 'application/json' - }, - status: 401, - }); - } - - switch (true) { - case method === 'GET': - return getMeterConsumption(req); - case method === 'OPTIONS': - return new Response('ok', { headers: corsHeaders }); - default: - return new Response(JSON.stringify({ error: 'Method not supported' }), { - headers: { - ...corsHeaders, - 'Content-Type': 'application/json' - }, - status: 400, - }); - } -}); - -/* To invoke locally: - - 1. Run `supabase start` (see: https://supabase.com/docs/reference/cli/supabase-start) - 2. Make an HTTP request: - - curl -i --location --request GET 'http://127.0.0.1:54321/functions/v1/meter-consumption?offset=0&pageLimit=20&FROM=2025-07-11T00:00:00.000Z&TO=2025-07-11T23:59:59.999Z' \ - --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0' \ - --header 'Content-Type: application/json' - -*/ diff --git a/supabase/functions/orders/index.ts b/supabase/functions/orders/index.ts deleted file mode 100644 index 65c5a50..0000000 --- a/supabase/functions/orders/index.ts +++ /dev/null @@ -1,96 +0,0 @@ -// Follow this setup guide to integrate the Deno language server with your editor: -// https://deno.land/manual/getting_started/setup_your_environment -// This enables autocomplete, go to definition, etc. - -// Setup type definitions for built-in Supabase Runtime APIs -import 'jsr:@supabase/functions-js/edge-runtime.d.ts'; -import { canAccessLenderApi, getContext } from '../_shared/auth.ts'; -import { corsHeaders } from '../_shared/cors.ts'; - -async function getTask(req, supabase, user) { - const isAuthorized = canAccessLenderApi(user); - - if(!isAuthorized) return new Response(JSON.stringify({ error: 'Not authorized' }), { - headers: { 'Content-Type': 'application/json' }, - status: 401, - }); - - const url = new URL(req.url); - - const page = Number(url.searchParams.get('page') || 1); - const size = Number(url.searchParams.get('size') || 100); - const order = url.searchParams.get('order') || 'id'; - const ascending = url.searchParams.get('ascending') || 'true'; - - const from = (page - 1) * size; - const to = from + size - 1; - - const { data, count: total, error } = await supabase - .from('orders') - .select('*, grids!inner(name)', { count: 'exact' }) - .eq('meta_is_hidden_from_reporting', false) - .eq('meta_order_type', 'ENERGY_TOPUP') - .eq('order_status', 'COMPLETED') - .order(order, { ascending: ascending === 'true' }) - .range(from, to); - - if(error) throw Error('An error occurred while trying to read the data'); - - const entries = data.map(entry => ({ - id: entry.id, - created_at: entry.created_at, - amount: entry.amount, - order_status: entry.order_status, - currency: entry.currency, - tariff: entry.tariff, - grid: entry.grids.name, - customer: '*******', - })); - - return new Response( - JSON.stringify({ - total, - page, - size, - total_pages: Math.ceil(total / size), - entries, - }), - { headers: { - ...corsHeaders, - 'Content-Type': 'application/json', - } }, - ); -} - -Deno.serve(async req => { - const { method } = req; - - // TODO: I do not like the idea of returning responses from functions - // being called here. Rather, a result should be called, and a response - // built in this very function with that result - switch (true) { - case method === 'GET': - // eslint-disable-next-line no-case-declarations - const { supabase, user } = await getContext(req); - return getTask(req, supabase, user); - case method === 'OPTIONS': - return new Response('ok', { headers: corsHeaders }); - default: - return new Response(JSON.stringify({ error: 'Method not supported' }), { - headers: { 'Content-Type': 'application/json' }, - status: 400, - }); - } -}); - -/* To invoke locally: - - 1. Run `supabase start` (see: https://supabase.com/docs/reference/cli/supabase-start) - 2. Make an HTTP request: - - curl -i --location --request POST 'http://127.0.0.1:54321/functions/v1/orders' \ - --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0' \ - --header 'Content-Type: application/json' \ - --data '{"name":"Functions"}' - -*/ diff --git a/supabase/migrations/20260710120000_init.sql b/supabase/migrations/20260710120000_init.sql new file mode 100644 index 0000000..93dd5f3 --- /dev/null +++ b/supabase/migrations/20260710120000_init.sql @@ -0,0 +1,3144 @@ +-- ============================================================================= +-- OSS baseline init migration +-- ============================================================================= +-- Canonical baseline schema for the NXT Grid mini-grid management platform, +-- derived from the legacy migration chain (legacy/supabase/migrations/) minus +-- dead/deprecated/company-specific objects. Every deviation from the legacy +-- chain is recorded in docs/plans/002-oss-migration/002b-schema-deviation-register.md +-- (register entries #1-#34 and the Column/Programmability/Performance/Data API/ +-- FK adjustment sections) — that file is the authoritative source for "why" any +-- given line here differs from the legacy chain. +-- ============================================================================= + +-- ============================================================================= +-- Extensions +-- ============================================================================= +-- Only extensions NOT already enabled on a fresh Supabase Postgres image are +-- created here. Platform defaults (pg_stat_statements, pgcrypto, +-- supabase_vault, uuid-ossp), pg_graphql, and advisor tooling (hypopg, +-- index_advisor) are intentionally omitted — see register #11. +-- pgjwt is intentionally NOT enabled: unavailable on Postgres 17 and confirmed +-- unused in the legacy schema/app code (register #11, amended 2026-07-10). + +CREATE EXTENSION IF NOT EXISTS "postgis" WITH SCHEMA "extensions"; + +CREATE EXTENSION IF NOT EXISTS "pg_net" WITH SCHEMA "extensions"; + +CREATE EXTENSION IF NOT EXISTS "pgsodium"; + +-- ============================================================================= +-- Enum types +-- ============================================================================= +-- 31 of the legacy chain's 42 enum types are kept. Dropped (register #1, #7, +-- #13, #14): directive_direction_enum, directive_error_enum, +-- directive_phase_enum, directive_special_status_enum, directive_status_enum, +-- directive_type_enum, meter_credit_transfer_status_enum, payout_status_enum, +-- pd_action_status_enum, pd_action_type_enum, pd_document_type_enum. +-- Value trims: external_system_enum drops JOTFORM/STEAMACO/ACREL (register +-- #28); notification_type_enum drops AUTO_PAYOUT_GENRATION_REPORT (register +-- #29). organization_type_enum gains PLATFORM_OPERATOR (register #22 — the +-- DB-native admin-organization flag; see rls_check_if_admin_org_member()). + +CREATE TYPE public.account_type_enum AS ENUM ( + 'AGENT', + 'MEMBER', + 'CUSTOMER' +); + +CREATE TYPE public.communication_protocol_enum AS ENUM ( + 'CALIN_V1', + 'CALIN_V2', + 'CALIN_LORAWAN' +); + +CREATE TYPE public.currency_enum AS ENUM ( + 'USD', + 'NGN', + 'EUR' +); + +-- external_system_enum: JOTFORM, STEAMACO, ACREL dropped (register #28) +CREATE TYPE public.external_system_enum AS ENUM ( + 'CALIN', + 'SOLCAST', + 'VICTRON', + 'FLUTTERWAVE', + 'AFRICASTALKING', + 'EPICOLLECT', + 'JIRA', + 'TELEGRAM', + 'ZEROTIER', + 'MAKE', + 'FLOW_XO', + 'SENDGRID' +); + +CREATE TYPE public.fs_command_type_enum AS ENUM ( + 'ON', + 'OFF' +); + +CREATE TYPE public.gender_enum AS ENUM ( + 'MALE', + 'FEMALE' +); + +CREATE TYPE public.generator_type_enum AS ENUM ( + 'SMALL', + 'LARGE' +); + +CREATE TYPE public.id_document_type_enum AS ENUM ( + 'PASSPORT', + 'NATIONAL_ID', + 'DRIVING_LICENSE', + 'VOTERS_CARD' +); + +CREATE TYPE public.issue_status_enum AS ENUM ( + 'OPEN', + 'CLOSED', + 'OVERRIDDEN' +); + +CREATE TYPE public.issue_type_enum AS ENUM ( + 'NO_COMMUNICATION', + 'METER_NOT_ACTIVATED', + 'TAMPER', + 'POWER_LIMIT_BREACHED', + 'OVER_VOLTAGE', + 'LOW_VOLTAGE', + 'POWER_LIMIT_BAD_CONFIGURATION', + 'METER_STATE_BAD_CONFIGURATION', + 'UNEXPECTED_POWER_LIMIT', + 'UNEXPECTED_METER_STATUS', + 'NO_CREDIT', + 'NO_CONSUMPTION', + 'NUMBER_OF_PHASES', + 'VEBUS_STATE', + 'VEBUS_ERROR', + 'QUATTRO_TEMPERATURE_ALARM', + 'QUATTRO_OVERLOAD_ALARM', + 'HIGH_BATTERY_TEMPERATURE_ALARM', + 'CELL_IMBALANCE_ALARM', + 'HIGH_CHARGE_CURRENT_ALARM', + 'HIGH_CHARGE_TEMPERATURE_ALARM', + 'BATTERY_INTERNAL_FAILURE', + 'BATTERY_CHARGE_BLOCKED_ALARM', + 'BATTERY_DISCHARGE_BLOCKED_ALARM' +); + +CREATE TYPE public.member_type_enum AS ENUM ( + 'SUPERADMIN', + 'ADMIN', + 'PARTNER', + 'FINANCE', + 'DEVELOPER', + 'MANAGER', + 'SUPPORT', + 'SERVICE', + 'SALES', + 'TECH' +); + +CREATE TYPE public.meter_commissioning_status_enum AS ENUM ( + 'PENDING', + 'PROCESSING', + 'SUCCESSFUL', + 'FAILED' +); + +CREATE TYPE public.meter_interaction_status_enum AS ENUM ( + 'QUEUED', + 'ABORTED', + 'PROCESSING', + 'SUCCESSFUL', + 'FAILED', + 'DEFERRED', + 'SUSPENDED' +); + +CREATE TYPE public.meter_interaction_type_enum AS ENUM ( + 'READ_CREDIT', + 'READ_POWER_LIMIT', + 'READ_VOLTAGE', + 'SET_POWER_LIMIT', + 'TOP_UP', + 'TURN_ON', + 'TURN_OFF', + 'READ_POWER', + 'READ_CURRENT', + 'CLEAR_CREDIT', + 'CLEAR_TAMPER', + 'READ_REPORT', + 'JOIN_NETWORK', + 'DELIVER_PREEXISTING_TOKEN', + 'READ_VERSION', + 'READ_DATE', + 'SET_DATE', + 'READ_TIME', + 'SET_TIME' +); + +CREATE TYPE public.meter_phase_enum AS ENUM ( + 'SINGLE_PHASE', + 'THREE_PHASE' +); + +CREATE TYPE public.meter_type_enum AS ENUM ( + 'HPS', + 'FS' +); + +CREATE TYPE public.mhi_operation_enum AS ENUM ( + 'ADD', + 'REMOVE' +); + +CREATE TYPE public.mhi_status_enum AS ENUM ( + 'PENDING', + 'PROCESSING', + 'SUCCESSFUL', + 'FAILED' +); + +CREATE TYPE public.mppt_type_enum AS ENUM ( + 'MPPT', + 'PV_INVERTER' +); + +CREATE TYPE public.notification_status_enum AS ENUM ( + 'PENDING', + 'PROCESSING', + 'RECEIVED_BY_API', + 'FAILED', + 'SUCCESSFUL', + 'READ', + 'UNKNOWN' +); + +-- notification_type_enum: AUTO_PAYOUT_GENRATION_REPORT dropped (register #29) +CREATE TYPE public.notification_type_enum AS ENUM ( + 'GRID_IS_HPS_ON_STATE_CHANGE', + 'GRID_IS_FS_ON_STATE_CHANGE', + 'GRID_METERING_HARDWARE_STATE_CHANGE', + 'FS_RULE_EXECUTION_COMING_UP', + 'FS_RULE_CHANGED', + 'TARIFF_RULE_CHANGED', + 'CLEAN_PANELS_REMINDER', + 'GRID_REVENUE', + 'PASSWORD_RESET', + 'INVITE', + 'CREDIT_SENT', + 'CREDIT_RECEIVED', + 'METER_TOPPED_UP', + 'PAYMENT_REJECTED', + 'SITE_SUBMISSION' +); + +CREATE TYPE public.order_actor_type_enum AS ENUM ( + 'BANKING_SYSTEM', + 'ORGANIZATION', + 'CONNECTION', + 'METER', + 'AGENT', + 'CUSTOMER' +); + +CREATE TYPE public.order_status_enum AS ENUM ( + 'INITIALISED', + 'PENDING', + 'COMPLETED', + 'FAILED', + 'CANCELLED', + 'TIMED_OUT', + 'IGNORED' +); + +CREATE TYPE public.order_type_enum AS ENUM ( + 'ENERGY_TOPUP', + 'CONNECTION_PAYMENT', + 'CONNECTION_REFUND', + 'AGENT_WITHDRAWAL', + 'AGENT_TOPUP', + 'ORGANIZATION_TOPUP', + 'ORGANIZATION_WITHDRAWAL', + 'CUSTOMER_TOPUP' +); + +-- organization_type_enum: PLATFORM_OPERATOR added (register #22) +CREATE TYPE public.organization_type_enum AS ENUM ( + 'SOLAR_DEVELOPER', + 'LENDER', + 'DATA_AGGREGATOR', + 'PLATFORM_OPERATOR' +); + +CREATE TYPE public.payment_channel_enum AS ENUM ( + 'USSD', + 'AYRTON', + 'NIFFLER', + 'TELEGRAM' +); + +CREATE TYPE public.payment_method_enum AS ENUM ( + 'CREDIT_CARD', + 'USSD', + 'BANK_TRANSFER' +); + +CREATE TYPE public.solcast_cache_request_type_enum AS ENUM ( + 'ESTIMATED_ACTUALS', + 'FORECAST' +); + +CREATE TYPE public.transaction_status_enum AS ENUM ( + 'SUCCESSFUL', + 'FAILED' +); + +CREATE TYPE public.wallet_type_enum AS ENUM ( + 'VIRTUAL', + 'REAL' +); + +CREATE TYPE public.weather_type_enum AS ENUM ( + 'CLOUDY', + 'CLOUDS', + 'SHOWERS', + 'SUNNY', + 'UNKNOWN', + 'CLOUDY_WITH_RAIN' +); + +-- ============================================================================= +-- Tables +-- ============================================================================= +-- 35 of the legacy chain's 57 tables are kept, carried over verbatim except +-- for the column drops/renames and table renames recorded in the deviation +-- register's "Column adjustments" section and register #16. + +CREATE TABLE public.accounts ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + full_name character varying, + email character varying, + phone character varying, + telegram_id character varying, + telegram_link_token character varying, + deleted_at timestamp(3) without time zone, + supabase_id uuid NOT NULL, + organization_id integer +); + +CREATE TABLE public.agents ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + grid_id integer, + account_id integer, + rls_organization_id integer +); + +CREATE TABLE public.api_keys ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + key character varying, + account_id integer +); + +CREATE TABLE public.audits ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + message character varying NOT NULL, + from_fs_command boolean DEFAULT false NOT NULL, + author_id integer, + grid_id integer, + organization_id integer, + meter_id integer, + agent_id integer, + customer_id integer, + member_id integer, + connection_id integer, + dcu_id integer +); + +CREATE TABLE public.banks ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + name character varying NOT NULL, + external_id character varying NOT NULL +); + +CREATE TABLE public.dcus ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + external_reference character varying NOT NULL, + external_system public.external_system_enum NOT NULL, + is_online boolean DEFAULT false NOT NULL, + last_online_at timestamp(3) with time zone, + is_online_updated_at timestamp(3) with time zone, + communication_protocol public.communication_protocol_enum, + grid_id integer, + last_metering_hardware_install_session_id integer, + rls_organization_id integer, + location_geom extensions.geometry(Point,4326) +); + +CREATE TABLE public.meters ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + external_reference character varying NOT NULL, + deleted_at timestamp(3) without time zone, + balance double precision, + balance_updated_at timestamp(3) with time zone, + kwh_credit_available double precision, + kwh_credit_available_updated_at timestamp(3) with time zone, + last_non_zero_consumption_at timestamp(3) with time zone, + is_on boolean DEFAULT false, + should_be_on boolean DEFAULT false, + is_on_updated_at timestamp(3) with time zone, + should_be_on_updated_at timestamp(3) with time zone, + is_manual_mode_on boolean DEFAULT false NOT NULL, + is_manual_mode_on_updated_at timestamp(3) with time zone, + voltage double precision, + voltage_updated_at timestamp(3) with time zone, + power double precision, + power_updated_at timestamp(3) with time zone, + latitude double precision, + longitude double precision, + coord_accuracy double precision DEFAULT '0'::double precision NOT NULL, + power_limit integer, + power_limit_updated_at timestamp(3) with time zone, + power_limit_should_be integer, + power_limit_should_be_updated_at timestamp(3) with time zone, + is_starred boolean DEFAULT false NOT NULL, + external_system public.external_system_enum NOT NULL, + meter_type public.meter_type_enum DEFAULT 'HPS'::public.meter_type_enum NOT NULL, + nickname character varying, + last_seen_at timestamp(3) with time zone, + issue_check_execution_session character varying, + issue_check_last_run_at timestamp(3) with time zone, + pole_id integer, + meter_phase public.meter_phase_enum DEFAULT 'SINGLE_PHASE'::public.meter_phase_enum NOT NULL, + last_metering_hardware_install_session_id integer, + kwh_tariff double precision, + version character varying, + power_limit_hps_mode integer DEFAULT 200 NOT NULL, + communication_protocol public.communication_protocol_enum DEFAULT 'CALIN_LORAWAN'::public.communication_protocol_enum, + is_cabin_meter boolean DEFAULT false NOT NULL, + last_encountered_issue_id integer, + connection_id integer, + dcu_id integer, + decoder_key text, + last_sts_token_issued_at timestamp with time zone, + rls_grid_id integer, + rls_organization_id integer, + is_test_mode_on boolean DEFAULT false NOT NULL, + connection_metrics jsonb +); + +CREATE TABLE public.poles ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + external_reference character varying(10) NOT NULL, + nickname character varying, + is_virtual boolean DEFAULT false NOT NULL, + location_accuracy double precision, + grid_id integer, + location_geom extensions.geometry(Point,4326) NOT NULL, + rls_organization_id integer +); + +CREATE TABLE public.connection_requested_meters ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + meter_type public.meter_type_enum NOT NULL, + meter_phase public.meter_phase_enum NOT NULL, + fee double precision DEFAULT '0'::double precision NOT NULL, + deleted_at timestamp(3) without time zone, + connection_id integer NOT NULL, + rls_organization_id integer +); + +CREATE TABLE public.connections ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + deleted_at timestamp(3) without time zone, + document_type public.id_document_type_enum DEFAULT 'PASSPORT'::public.id_document_type_enum, + document_id character varying, + upload_uuid character varying, + external_system public.external_system_enum DEFAULT 'EPICOLLECT'::public.external_system_enum, + paid double precision DEFAULT '0'::double precision NOT NULL, + currency public.currency_enum NOT NULL, + women_impacted integer NOT NULL, + is_lifeline boolean, + is_public boolean NOT NULL, + is_commercial boolean NOT NULL, + is_residential boolean NOT NULL, + is_building_wired boolean DEFAULT false, + is_using_led_bulbs boolean DEFAULT false, + customer_id integer, + rls_organization_id integer +); + +CREATE TABLE public.customers ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + gender public.gender_enum, + total_connection_fee double precision DEFAULT '0'::double precision NOT NULL, + total_connection_paid double precision DEFAULT '0'::double precision NOT NULL, + is_hidden_from_reporting boolean DEFAULT false NOT NULL, + lives_primarily_in_the_community boolean DEFAULT true NOT NULL, + latitude double precision, + longitude double precision, + generator_owned public.generator_type_enum, + grid_id integer, + account_id integer, + rls_organization_id integer +); + +CREATE TABLE public.meter_command_batch_executions ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + pending_count integer DEFAULT 0 NOT NULL, + processing_count integer DEFAULT 0 NOT NULL, + successful_count integer DEFAULT 0 NOT NULL, + failed_count integer DEFAULT 0 NOT NULL, + processed_count integer DEFAULT 0 NOT NULL, + total_count integer DEFAULT 0 NOT NULL, + meter_command_batch_id integer NOT NULL, + rls_organization_id integer, + completed_at timestamp with time zone, + qualified_at timestamp with time zone +); + +CREATE TABLE public.meter_command_batches ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + is_deleted boolean DEFAULT false NOT NULL, + hour integer NOT NULL, + minute integer NOT NULL, + is_repeating boolean DEFAULT false NOT NULL, + grid_id integer, + fs_command public.fs_command_type_enum, + author_id integer, + updated_at timestamp(3) with time zone, + rls_organization_id integer, + task_type public.meter_interaction_type_enum +); + +CREATE TABLE public.energy_cabins ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + grid_id integer NOT NULL, + location_geom extensions.geometry(Point,4326) NOT NULL, + rls_organization_id integer +); + +CREATE TABLE public.grids ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + deployed_at timestamp(3) with time zone, + commissioned_at timestamp(3) with time zone, + name character varying NOT NULL, + is_hps_on boolean DEFAULT false NOT NULL, + is_hps_on_updated_at timestamp(3) with time zone, + walkthrough_external_id character varying, + timezone character varying DEFAULT 'UTC'::character varying NOT NULL, + kwp double precision DEFAULT '0'::double precision NOT NULL, + kwh double precision DEFAULT '0'::double precision NOT NULL, + kwp_tariff double precision DEFAULT '0'::double precision NOT NULL, + kwh_tariff double precision DEFAULT '0'::double precision NOT NULL, + kwh_tariff_essential_service double precision DEFAULT '0'::double precision NOT NULL, + kwh_tariff_full_service double precision DEFAULT '0'::double precision NOT NULL, + current_weather public.weather_type_enum, + generation_external_system public.external_system_enum DEFAULT 'VICTRON'::public.external_system_enum NOT NULL, + metering_external_system public.external_system_enum DEFAULT 'CALIN'::public.external_system_enum NOT NULL, + generation_external_site_id character varying, + generation_external_gateway_id character varying, + generation_gateway_last_seen_at timestamp(3) with time zone, + is_fs_on boolean DEFAULT false NOT NULL, + is_fs_on_updated_at timestamp(3) with time zone, + should_fs_be_on boolean DEFAULT false NOT NULL, + should_fs_be_on_updated_at timestamp(3) with time zone, + default_hps_connection_fee double precision DEFAULT '0'::double precision NOT NULL, + default_fs_1_phase_connection_fee double precision DEFAULT '0'::double precision NOT NULL, + default_fs_3_phase_connection_fee double precision DEFAULT '0'::double precision NOT NULL, + monthly_rental double precision DEFAULT '0'::double precision NOT NULL, + is_hidden_from_reporting boolean DEFAULT true NOT NULL, + is_three_phase_supported boolean DEFAULT false NOT NULL, + is_using_vsat boolean DEFAULT false NOT NULL, + is_using_mobile_network boolean DEFAULT false NOT NULL, + is_hps_on_threshold_kw double precision DEFAULT 0 NOT NULL, + kwh_per_battery_module double precision, + identifier integer, + lifeline_connection_kwh_threshold integer DEFAULT 5 NOT NULL, + lifeline_connection_days_threshold integer DEFAULT 30 NOT NULL, + is_panel_cleaning_notification_enabled boolean DEFAULT false NOT NULL, + is_energised_notification_enabled boolean DEFAULT false NOT NULL, + is_fs_on_notification_enabled boolean DEFAULT false NOT NULL, + is_metering_hardware_online_notification_enabled boolean DEFAULT false NOT NULL, + is_tariff_change_notification_enabled boolean DEFAULT false NOT NULL, + is_upcoming_fs_control_rule_notification_enabled boolean DEFAULT false NOT NULL, + is_fs_control_rule_change_notification_enabled boolean DEFAULT false NOT NULL, + is_automatic_meter_install_enabled boolean DEFAULT false NOT NULL, + is_automatic_energy_generation_data_sync_enabled boolean DEFAULT false NOT NULL, + is_automatic_meter_energy_consumption_data_sync_enabled boolean DEFAULT false NOT NULL, + is_dcu_connectivity_tracking_enabled boolean DEFAULT false NOT NULL, + is_router_connectivity_tracking_enabled boolean DEFAULT false NOT NULL, + is_cabin_meter_credit_depleting boolean DEFAULT false NOT NULL, + telegram_response_path_token character varying, + telegram_notification_channel_invite_link character varying, + internal_telegram_group_chat_id character varying, + internal_telegram_group_thread_id character varying, + organization_id integer NOT NULL, + meter_commissioning_initial_credit_kwh real DEFAULT '0'::real NOT NULL, + is_generation_managed_by_nxt_grid boolean DEFAULT true NOT NULL, + location_geom extensions.geometry(Point,4326), + telegram_config jsonb DEFAULT '{}'::jsonb, + feature_access_config jsonb DEFAULT '{}'::jsonb NOT NULL +); + +CREATE TABLE public.issues ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + issue_type public.issue_type_enum NOT NULL, + issue_status public.issue_status_enum DEFAULT 'OPEN'::public.issue_status_enum NOT NULL, + external_tracking_system public.external_system_enum DEFAULT 'JIRA'::public.external_system_enum NOT NULL, + started_at timestamp(3) with time zone, + closed_at timestamp(3) with time zone, + external_tracking_reference character varying, + meter_id integer, + rls_organization_id integer +); + +CREATE TABLE public.members ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + subscribed_to_telegram_revenue_notifications boolean DEFAULT false NOT NULL, + member_type public.member_type_enum DEFAULT 'DEVELOPER'::public.member_type_enum NOT NULL, + account_id integer, + busy_commissioning_id integer, + training_level smallint DEFAULT '0'::smallint NOT NULL, + rls_organization_id integer, + hidden boolean DEFAULT false NOT NULL +); + +CREATE TABLE public.meter_commissionings ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + meter_commissioning_status public.meter_commissioning_status_enum DEFAULT 'PROCESSING'::public.meter_commissioning_status_enum NOT NULL, + metering_hardware_install_session_id integer, + rls_organization_id integer +); + +CREATE TABLE public.meter_interactions ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + meter_id integer NOT NULL, + token text, + order_id integer, + transactive_kwh real, + target_power_limit integer, + result_value jsonb, + meter_interaction_type public.meter_interaction_type_enum NOT NULL, + meter_interaction_status public.meter_interaction_status_enum DEFAULT 'QUEUED'::public.meter_interaction_status_enum NOT NULL, + batch_execution_id integer, + meter_commissioning_id integer, + delivery_failure_history jsonb, + payload_data jsonb +); + +CREATE TABLE public.metering_hardware_imports ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + metering_hardware_import_operation public.mhi_operation_enum DEFAULT 'ADD'::public.mhi_operation_enum NOT NULL, + metering_hardware_import_status public.mhi_status_enum DEFAULT 'PENDING'::public.mhi_status_enum NOT NULL, + metering_hardware_install_session_id integer NOT NULL, + lock_session character varying, + rls_organization_id integer +); + +CREATE TABLE public.metering_hardware_install_sessions ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + dcu_id integer, + meter_id integer, + author_id integer, + last_meter_commissioning_id integer, + last_metering_hardware_import_id integer, + rls_organization_id integer +); + +CREATE TABLE public.mppts ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + external_reference character varying NOT NULL, + external_id character varying NOT NULL, + external_system public.external_system_enum DEFAULT 'VICTRON'::public.external_system_enum NOT NULL, + kw double precision, + azimuth double precision, + tilt double precision, + installed_at timestamp(3) with time zone DEFAULT now() NOT NULL, + deleted_at timestamp(3) with time zone, + mppt_type public.mppt_type_enum DEFAULT 'MPPT'::public.mppt_type_enum NOT NULL, + grid_id integer, + rls_organization_id integer +); + +CREATE TABLE public.notes ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + message character varying, + customer_id integer, + connection_id integer, + meter_id integer, + author_id integer, + rls_organization_id integer +); + +CREATE TABLE public.notification_parameters ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + parameters json +); + +CREATE TABLE public.notifications ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + connector_external_system public.external_system_enum, + carrier_external_system public.external_system_enum, + notification_type public.notification_type_enum NOT NULL, + notification_status public.notification_status_enum NOT NULL, + external_reference character varying, + notification_parameter_id integer, + grid_id integer, + organization_id integer, + account_id integer, + lock_session character varying, + message character varying, + phone character varying, + subject character varying, + email character varying, + chat_id character varying, + thread_id character varying +); + +CREATE TABLE public.orders ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + amount double precision NOT NULL, + order_status public.order_status_enum DEFAULT 'PENDING'::public.order_status_enum NOT NULL, + lock_session character varying, + external_reference character varying, + currency public.currency_enum NOT NULL, + tariff_type public.meter_type_enum, + tariff double precision DEFAULT '-1'::double precision NOT NULL, + payment_method public.payment_method_enum, + payment_channel public.payment_channel_enum, + author_id integer, + historical_grid_id integer, + meta_author_type public.account_type_enum, + meta_author_name character varying, + meta_author_id integer, + meta_order_type public.order_type_enum, + meta_sender_id integer, + meta_sender_name character varying, + meta_receiver_name character varying, + meta_receiver_name_part_2 character varying, + meta_sender_name_part_2 character varying, + meta_receiver_id integer, + meta_sender_type public.order_actor_type_enum, + meta_receiver_type public.order_actor_type_enum, + meta_is_hidden_from_reporting boolean, + sender_wallet_id integer, + receiver_wallet_id integer, + ussd_session_id integer, + meta_receiver_id_part_2 integer, + rls_organization_id integer, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + +CREATE TABLE public.organizations ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + name character varying NOT NULL, + formal_name character varying, + email character varying, + epicollect_contract_survey_slug character varying, + epicollect_contract_survey_secret character varying, + epicollect_contract_survey_client_id character varying, + epicollect_contract_last_sync_at timestamp(3) with time zone, + developer_group_telegram_chat_id character varying, + deleted_at timestamp with time zone, + organization_type public.organization_type_enum DEFAULT 'SOLAR_DEVELOPER'::public.organization_type_enum NOT NULL +); + +CREATE TABLE public.pd_site_submissions ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + author_full_name text NOT NULL, + author_email text NOT NULL, + author_organization_name text NOT NULL, + site_name character varying NOT NULL, + site_details jsonb NOT NULL, + outline_geom extensions.geometry(Polygon,4326), + deleted_at timestamp with time zone, + location_geom extensions.geometry(Point,4326), + organization_id integer, + author_organization_id integer, + buildings_geo_flat jsonb, + distribution_geo_flat jsonb, + meta_geo_flat jsonb, + poles_geo_flat jsonb +); + +CREATE TABLE public.pd_sites ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + organization_id integer, + name text NOT NULL, + deleted_at timestamp with time zone, + location_geom extensions.geometry(Point,4326), + outline_geom extensions.geometry(Polygon,4326), + operations_grid_id integer +); + +CREATE TABLE public.routers ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + external_reference character varying NOT NULL, + external_system public.external_system_enum DEFAULT 'ZEROTIER'::public.external_system_enum NOT NULL, + is_online boolean DEFAULT false NOT NULL, + is_online_updated_at timestamp(3) with time zone, + deleted_at timestamp(3) with time zone, + grid_id integer, + rls_organization_id integer +); + +CREATE TABLE public.solcast_cache ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + request_type public.solcast_cache_request_type_enum NOT NULL, + latitude numeric(8,6) NOT NULL, + longitude numeric(9,6) NOT NULL, + tilt numeric(10,3) NOT NULL, + azimuth numeric(10,3) NOT NULL, + capacity_kwp numeric(10,3) NOT NULL, + install_date character varying NOT NULL, + response text NOT NULL +); + +CREATE TABLE public.transactions ( + id bigint NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + amount double precision NOT NULL, + transaction_status public.transaction_status_enum NOT NULL, + balance_before double precision DEFAULT '-1'::double precision NOT NULL, + balance_after double precision DEFAULT '-1'::double precision NOT NULL, + wallet_id integer, + order_id integer, + rls_organization_id integer +); + +CREATE TABLE public.ussd_session_hops ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + text character varying, + phone character varying, + network_code character varying, + service_code character varying, + ussd_session_id integer +); + +CREATE TABLE public.ussd_sessions ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + phone character varying NOT NULL, + amount double precision, + external_reference character varying, + external_system public.external_system_enum NOT NULL, + is_using_other_option boolean DEFAULT false NOT NULL, + account_id integer, + meter_id integer, + bank_id integer +); + +CREATE TABLE public.wallets ( + id integer NOT NULL, + created_at timestamp(3) with time zone DEFAULT now() NOT NULL, + organization_id integer, + agent_id integer, + customer_id integer, + meter_id integer, + lock_session character varying, + balance double precision DEFAULT '0'::double precision NOT NULL, + balance_updated_at timestamp(3) with time zone, + identifier character varying, + wallet_type public.wallet_type_enum DEFAULT 'REAL'::public.wallet_type_enum NOT NULL, + connection_id integer, + rls_organization_id integer +); + +-- ============================================================================= +-- Sequences +-- ============================================================================= +-- One sequence (or identity-column sequence) per kept table's primary key. +-- 3 tables (meter_interactions, pd_sites, pd_site_submissions) use identity +-- columns rather than a standalone sequence + DEFAULT nextval(); pg_dump +-- renders both forms, kept as dumped. + +CREATE SEQUENCE public.accounts_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.accounts ALTER COLUMN id SET DEFAULT nextval('public.accounts_id_seq'::regclass); + +ALTER SEQUENCE public.accounts_id_seq OWNED BY public.accounts.id; + +CREATE SEQUENCE public.agents_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.agents ALTER COLUMN id SET DEFAULT nextval('public.agents_id_seq'::regclass); + +ALTER SEQUENCE public.agents_id_seq OWNED BY public.agents.id; + +CREATE SEQUENCE public.api_keys_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.api_keys ALTER COLUMN id SET DEFAULT nextval('public.api_keys_id_seq'::regclass); + +ALTER SEQUENCE public.api_keys_id_seq OWNED BY public.api_keys.id; + +CREATE SEQUENCE public.audits_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.audits ALTER COLUMN id SET DEFAULT nextval('public.audits_id_seq'::regclass); + +ALTER SEQUENCE public.audits_id_seq OWNED BY public.audits.id; + +CREATE SEQUENCE public.banks_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.banks ALTER COLUMN id SET DEFAULT nextval('public.banks_id_seq'::regclass); + +ALTER SEQUENCE public.banks_id_seq OWNED BY public.banks.id; + +CREATE SEQUENCE public.dcus_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.dcus ALTER COLUMN id SET DEFAULT nextval('public.dcus_id_seq'::regclass); + +ALTER SEQUENCE public.dcus_id_seq OWNED BY public.dcus.id; + +CREATE SEQUENCE public.meters_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.meters ALTER COLUMN id SET DEFAULT nextval('public.meters_id_seq'::regclass); + +ALTER SEQUENCE public.meters_id_seq OWNED BY public.meters.id; + +CREATE SEQUENCE public.poles_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.poles ALTER COLUMN id SET DEFAULT nextval('public.poles_id_seq'::regclass); + +ALTER SEQUENCE public.poles_id_seq OWNED BY public.poles.id; + +CREATE SEQUENCE public.connection_requested_meters_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.connection_requested_meters ALTER COLUMN id SET DEFAULT nextval('public.connection_requested_meters_id_seq'::regclass); + +ALTER SEQUENCE public.connection_requested_meters_id_seq OWNED BY public.connection_requested_meters.id; + +CREATE SEQUENCE public.connections_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.connections ALTER COLUMN id SET DEFAULT nextval('public.connections_id_seq'::regclass); + +ALTER SEQUENCE public.connections_id_seq OWNED BY public.connections.id; + +CREATE SEQUENCE public.customers_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.customers ALTER COLUMN id SET DEFAULT nextval('public.customers_id_seq'::regclass); + +ALTER SEQUENCE public.customers_id_seq OWNED BY public.customers.id; + +CREATE SEQUENCE public.meter_command_batch_executions_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.meter_command_batch_executions ALTER COLUMN id SET DEFAULT nextval('public.meter_command_batch_executions_id_seq'::regclass); + +ALTER SEQUENCE public.meter_command_batch_executions_id_seq OWNED BY public.meter_command_batch_executions.id; + +CREATE SEQUENCE public.meter_command_batches_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.meter_command_batches ALTER COLUMN id SET DEFAULT nextval('public.meter_command_batches_id_seq'::regclass); + +ALTER SEQUENCE public.meter_command_batches_id_seq OWNED BY public.meter_command_batches.id; + +CREATE SEQUENCE public.energy_cabins_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.energy_cabins ALTER COLUMN id SET DEFAULT nextval('public.energy_cabins_id_seq'::regclass); + +ALTER SEQUENCE public.energy_cabins_id_seq OWNED BY public.energy_cabins.id; + +CREATE SEQUENCE public.grids_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.grids ALTER COLUMN id SET DEFAULT nextval('public.grids_id_seq'::regclass); + +ALTER SEQUENCE public.grids_id_seq OWNED BY public.grids.id; + +CREATE SEQUENCE public.issues_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.issues ALTER COLUMN id SET DEFAULT nextval('public.issues_id_seq'::regclass); + +ALTER SEQUENCE public.issues_id_seq OWNED BY public.issues.id; + +CREATE SEQUENCE public.members_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.members ALTER COLUMN id SET DEFAULT nextval('public.members_id_seq'::regclass); + +ALTER SEQUENCE public.members_id_seq OWNED BY public.members.id; + +CREATE SEQUENCE public.meter_commissionings_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.meter_commissionings ALTER COLUMN id SET DEFAULT nextval('public.meter_commissionings_id_seq'::regclass); + +ALTER SEQUENCE public.meter_commissionings_id_seq OWNED BY public.meter_commissionings.id; + +ALTER TABLE public.meter_interactions ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.meter_interactions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + +CREATE SEQUENCE public.metering_hardware_imports_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.metering_hardware_imports ALTER COLUMN id SET DEFAULT nextval('public.metering_hardware_imports_id_seq'::regclass); + +ALTER SEQUENCE public.metering_hardware_imports_id_seq OWNED BY public.metering_hardware_imports.id; + +CREATE SEQUENCE public.metering_hardware_install_sessions_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.metering_hardware_install_sessions ALTER COLUMN id SET DEFAULT nextval('public.metering_hardware_install_sessions_id_seq'::regclass); + +ALTER SEQUENCE public.metering_hardware_install_sessions_id_seq OWNED BY public.metering_hardware_install_sessions.id; + +CREATE SEQUENCE public.mppts_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.mppts ALTER COLUMN id SET DEFAULT nextval('public.mppts_id_seq'::regclass); + +ALTER SEQUENCE public.mppts_id_seq OWNED BY public.mppts.id; + +CREATE SEQUENCE public.notes_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.notes ALTER COLUMN id SET DEFAULT nextval('public.notes_id_seq'::regclass); + +ALTER SEQUENCE public.notes_id_seq OWNED BY public.notes.id; + +CREATE SEQUENCE public.notification_parameters_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.notification_parameters ALTER COLUMN id SET DEFAULT nextval('public.notification_parameters_id_seq'::regclass); + +ALTER SEQUENCE public.notification_parameters_id_seq OWNED BY public.notification_parameters.id; + +CREATE SEQUENCE public.notifications_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.notifications ALTER COLUMN id SET DEFAULT nextval('public.notifications_id_seq'::regclass); + +ALTER SEQUENCE public.notifications_id_seq OWNED BY public.notifications.id; + +CREATE SEQUENCE public.orders_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.orders ALTER COLUMN id SET DEFAULT nextval('public.orders_id_seq'::regclass); + +ALTER SEQUENCE public.orders_id_seq OWNED BY public.orders.id; + +CREATE SEQUENCE public.organizations_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.organizations ALTER COLUMN id SET DEFAULT nextval('public.organizations_id_seq'::regclass); + +ALTER SEQUENCE public.organizations_id_seq OWNED BY public.organizations.id; + +ALTER TABLE public.pd_site_submissions ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.pd_site_submissions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + +ALTER TABLE public.pd_sites ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.pd_sites_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + +CREATE SEQUENCE public.routers_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.routers ALTER COLUMN id SET DEFAULT nextval('public.routers_id_seq'::regclass); + +ALTER SEQUENCE public.routers_id_seq OWNED BY public.routers.id; + +CREATE SEQUENCE public.solcast_cache_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.solcast_cache ALTER COLUMN id SET DEFAULT nextval('public.solcast_cache_id_seq'::regclass); + +ALTER SEQUENCE public.solcast_cache_id_seq OWNED BY public.solcast_cache.id; + +CREATE SEQUENCE public.transactions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.transactions ALTER COLUMN id SET DEFAULT nextval('public.transactions_id_seq'::regclass); + +ALTER SEQUENCE public.transactions_id_seq OWNED BY public.transactions.id; + +CREATE SEQUENCE public.ussd_session_hops_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.ussd_session_hops ALTER COLUMN id SET DEFAULT nextval('public.ussd_session_hops_id_seq'::regclass); + +ALTER SEQUENCE public.ussd_session_hops_id_seq OWNED BY public.ussd_session_hops.id; + +CREATE SEQUENCE public.ussd_sessions_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.ussd_sessions ALTER COLUMN id SET DEFAULT nextval('public.ussd_sessions_id_seq'::regclass); + +ALTER SEQUENCE public.ussd_sessions_id_seq OWNED BY public.ussd_sessions.id; + +CREATE SEQUENCE public.wallets_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE ONLY public.wallets ALTER COLUMN id SET DEFAULT nextval('public.wallets_id_seq'::regclass); + +ALTER SEQUENCE public.wallets_id_seq OWNED BY public.wallets.id; + +-- ============================================================================= +-- Views +-- ============================================================================= +-- 3 of the legacy chain's 4 views are kept (agents_with_account, +-- customers_with_account, meters_with_account_and_statuses), with the same +-- column drops applied as their underlying tables. batch_commands is dropped +-- (register #9 — read path for the dropped directive-batch tables). + +CREATE VIEW public.agents_with_account WITH (security_invoker='true') AS + SELECT agents.id, + agents.created_at, + agents.grid_id, + agents.account_id, + accounts.full_name, + accounts.phone, + accounts.deleted_at + FROM (public.agents + JOIN public.accounts ON ((agents.account_id = accounts.id))); + +CREATE VIEW public.customers_with_account WITH (security_invoker='true') AS + SELECT c.id, + c.created_at, + c.gender, + c.total_connection_fee, + c.total_connection_paid, + c.is_hidden_from_reporting, + c.lives_primarily_in_the_community, + c.latitude, + c.longitude, + c.generator_owned, + c.grid_id, + c.account_id, + a.full_name, + a.phone, + a.deleted_at, + (c.total_connection_paid >= c.total_connection_fee) AS has_fully_paid_connection_fees, + ( SELECT string_agg((m.external_reference)::text, ', '::text) AS string_agg + FROM (public.connections conn + JOIN public.meters m ON ((m.connection_id = conn.id))) + WHERE (conn.customer_id = c.id)) AS meter + FROM (public.customers c + JOIN public.accounts a ON ((c.account_id = a.id))); + +CREATE VIEW public.meters_with_account_and_statuses WITH (security_invoker='true') AS + SELECT m.id, + m.created_at, + m.external_reference, + m.deleted_at, + m.balance, + m.balance_updated_at, + m.kwh_credit_available, + m.kwh_credit_available_updated_at, + m.last_non_zero_consumption_at, + m.is_on, + m.should_be_on, + m.is_on_updated_at, + m.should_be_on_updated_at, + m.is_manual_mode_on, + m.is_manual_mode_on_updated_at, + m.voltage, + m.voltage_updated_at, + m.power, + m.power_updated_at, + m.latitude, + m.longitude, + m.coord_accuracy, + m.power_limit, + m.power_limit_updated_at, + m.power_limit_should_be, + m.power_limit_should_be_updated_at, + m.is_starred, + m.external_system, + m.meter_type, + m.nickname, + m.last_seen_at, + m.issue_check_execution_session, + m.issue_check_last_run_at, + m.pole_id, + m.meter_phase, + m.last_metering_hardware_install_session_id, + m.kwh_tariff, + m.version, + m.power_limit_hps_mode, + m.communication_protocol, + m.is_cabin_meter, + m.last_encountered_issue_id, + m.connection_id, + m.dcu_id, + m.decoder_key, + m.last_sts_token_issued_at, + m.rls_grid_id, + m.rls_organization_id, + m.is_test_mode_on, + c.grid_id, + c.is_hidden_from_reporting, + a.full_name, + a.phone, + ( + CASE + WHEN (m.last_metering_hardware_install_session_id IS NULL) THEN 'SUCCESSFUL'::text + WHEN ((i.metering_hardware_import_status = 'PENDING'::public.mhi_status_enum) OR (com.meter_commissioning_status = 'PENDING'::public.meter_commissioning_status_enum)) THEN 'PENDING'::text + WHEN ((i.metering_hardware_import_status = 'PROCESSING'::public.mhi_status_enum) OR (com.meter_commissioning_status = 'PROCESSING'::public.meter_commissioning_status_enum)) THEN 'PROCESSING'::text + WHEN (com.meter_commissioning_status = 'SUCCESSFUL'::public.meter_commissioning_status_enum) THEN 'SUCCESSFUL'::text + WHEN ((i.metering_hardware_import_status = 'FAILED'::public.mhi_status_enum) OR (com.meter_commissioning_status = 'FAILED'::public.meter_commissioning_status_enum) OR ((i.metering_hardware_import_status = 'SUCCESSFUL'::public.mhi_status_enum) AND (com.meter_commissioning_status IS NULL))) THEN 'FAILED'::text + ELSE NULL::text + END)::public.mhi_status_enum AS install_status, + CASE + WHEN (iss.issue_status = 'OPEN'::public.issue_status_enum) THEN iss.issue_type + ELSE NULL::public.issue_type_enum + END AS open_issue + FROM (((((((public.meters m + JOIN public.connections conn ON ((m.connection_id = conn.id))) + JOIN public.customers c ON ((conn.customer_id = c.id))) + JOIN public.accounts a ON ((c.account_id = a.id))) + LEFT JOIN public.metering_hardware_install_sessions install_session ON ((m.last_metering_hardware_install_session_id = install_session.id))) + LEFT JOIN public.metering_hardware_imports i ON ((install_session.last_metering_hardware_import_id = i.id))) + LEFT JOIN public.meter_commissionings com ON ((install_session.last_meter_commissioning_id = com.id))) + LEFT JOIN public.issues iss ON ((m.last_encountered_issue_id = iss.id))); + +-- ============================================================================= +-- Constraints +-- ============================================================================= +-- Primary key and unique constraints first, then foreign keys. +-- FK cycle hardening (register #34): 5 denormalized "latest pointer" FKs get +-- ON DELETE SET NULL so a referenced row's deletion doesn't require deleting +-- the pointer's owner; their 5 structural back-pointer counterparts are left +-- at the implicit NO ACTION default (deleting the pointed-to row must fail, +-- or cascade through the owning aggregate root, unless the pointer is +-- cleared first). + +ALTER TABLE ONLY public.connections + ADD CONSTRAINT "PK_0a1f844af3122354cbd487a8d03" PRIMARY KEY (id); + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "PK_0a71b52dbb545fa36efaf070583" PRIMARY KEY (id); + +ALTER TABLE ONLY public.customers + ADD CONSTRAINT "PK_133ec679a801fab5e070f73d3ea" PRIMARY KEY (id); + +ALTER TABLE ONLY public.poles + ADD CONSTRAINT "PK_1f4336016e8de1d62acb05ce829" PRIMARY KEY (id); + +ALTER TABLE ONLY public.members + ADD CONSTRAINT "PK_28b53062261b996d9c99fa12404" PRIMARY KEY (id); + +ALTER TABLE ONLY public.notification_parameters + ADD CONSTRAINT "PK_338f36e72b69406bed36e054e09" PRIMARY KEY (id); + +ALTER TABLE ONLY public.banks + ADD CONSTRAINT "PK_3975b5f684ec241e3901db62d77" PRIMARY KEY (id); + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "PK_4eb998262e5c773a89dfe3ea0e1" PRIMARY KEY (id); + +ALTER TABLE ONLY public.ussd_session_hops + ADD CONSTRAINT "PK_56d984bae21ce6e86848a8a3c04" PRIMARY KEY (id); + +ALTER TABLE ONLY public.energy_cabins + ADD CONSTRAINT "PK_59ecb7965974e45fdc2f243bda7" PRIMARY KEY (id); + +ALTER TABLE ONLY public.accounts + ADD CONSTRAINT "PK_5a7a02c20412299d198e097a8fe" PRIMARY KEY (id); + +ALTER TABLE ONLY public.api_keys + ADD CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad" PRIMARY KEY (id); + +ALTER TABLE ONLY public.metering_hardware_imports + ADD CONSTRAINT "PK_665b32b395a7675648ed77da4f6" PRIMARY KEY (id); + +ALTER TABLE ONLY public.meter_commissionings + ADD CONSTRAINT "PK_69fce91302a5dc8dd3db274717b" PRIMARY KEY (id); + +ALTER TABLE ONLY public.notifications + ADD CONSTRAINT "PK_6a72c3c0f683f6462415e653c3a" PRIMARY KEY (id); + +ALTER TABLE ONLY public.organizations + ADD CONSTRAINT "PK_6b031fcd0863e3f6b44230163f9" PRIMARY KEY (id); + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "PK_710e2d4957aa5878dfe94e4ac2f" PRIMARY KEY (id); + +ALTER TABLE ONLY public.dcus + ADD CONSTRAINT "PK_82192607fa5119101a78fe53b7d" PRIMARY KEY (id); + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "PK_8402e5df5a30a229380e83e4f7e" PRIMARY KEY (id); + +ALTER TABLE ONLY public.grids + ADD CONSTRAINT "PK_840d40fdcde1e935afd43082aca" PRIMARY KEY (id); + +ALTER TABLE ONLY public.meter_command_batches + ADD CONSTRAINT "PK_92d424811a3fd49a8020eadb97a" PRIMARY KEY (id); + +ALTER TABLE ONLY public.meter_command_batch_executions + ADD CONSTRAINT "PK_9494ab23abdfe4e59f3f29842d5" PRIMARY KEY (id); + +ALTER TABLE ONLY public.agents + ADD CONSTRAINT "PK_9c653f28ae19c5884d5baf6a1d9" PRIMARY KEY (id); + +ALTER TABLE ONLY public.issues + ADD CONSTRAINT "PK_9d8ecbbeff46229c700f0449257" PRIMARY KEY (id); + +ALTER TABLE ONLY public.solcast_cache + ADD CONSTRAINT "PK_a0b1d6bdbc5ca0056201a1c6dd0" PRIMARY KEY (id); + +ALTER TABLE ONLY public.transactions + ADD CONSTRAINT "PK_a219afd8dd77ed80f5a862f1db9" PRIMARY KEY (id); + +ALTER TABLE ONLY public.notes + ADD CONSTRAINT "PK_af6206538ea96c4e77e9f400c3d" PRIMARY KEY (id); + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "PK_b2d7a2089999197dc7024820f28" PRIMARY KEY (id); + +ALTER TABLE ONLY public.routers + ADD CONSTRAINT "PK_b6d283f1e40d4942dedbc0cb27a" PRIMARY KEY (id); + +ALTER TABLE ONLY public.ussd_sessions + ADD CONSTRAINT "PK_c18f16f36e79b2fb87783476830" PRIMARY KEY (id); + +ALTER TABLE ONLY public.mppts + ADD CONSTRAINT "PK_c8a4dc57b932c173f6c579804c0" PRIMARY KEY (id); + +ALTER TABLE ONLY public.connection_requested_meters + ADD CONSTRAINT "PK_ef90f8d1aa3055757e0ff8e4aa7" PRIMARY KEY (id); + +ALTER TABLE ONLY public.accounts + ADD CONSTRAINT "REL_13bf998d703e7d4c7b9a90f4f7" UNIQUE (supabase_id); + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "REL_4eb64f5decb7250cfa993410c1" UNIQUE (last_meter_commissioning_id); + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "REL_5606d6ec5ab568377509edc526" UNIQUE (last_encountered_issue_id); + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "REL_6580899a2293de27787376887f" UNIQUE (customer_id); + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "REL_86d4557a79e374b5c55cb3b66d" UNIQUE (last_metering_hardware_install_session_id); + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "REL_8db1f4e4f8122bd25d50ad96b2" UNIQUE (meter_id); + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "REL_92081072063eaf51300ab6c267" UNIQUE (ussd_session_id); + +ALTER TABLE ONLY public.dcus + ADD CONSTRAINT "REL_9334b587e6e5291f8ccd1b11c4" UNIQUE (last_metering_hardware_install_session_id); + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "REL_be64a09dc738420a409cb96026" UNIQUE (last_metering_hardware_import_id); + +ALTER TABLE ONLY public.agents + ADD CONSTRAINT "REL_df520decc9e003a843d8edd986" UNIQUE (account_id); + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "REL_e63e504d8e35ef37a2c56b75eb" UNIQUE (connection_id); + +ALTER TABLE ONLY public.customers + ADD CONSTRAINT "REL_ebcc29963874e55053e8ee80be" UNIQUE (account_id); + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "REL_f499c61c6d6a0ac3f794d966ed" UNIQUE (organization_id); + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "REL_f5782e05e8688f0cfbb5c4a52c" UNIQUE (agent_id); + +ALTER TABLE ONLY public.members + ADD CONSTRAINT "REL_fd9dfb97e21b75fc45d42aa614" UNIQUE (account_id); + +ALTER TABLE ONLY public.accounts + ADD CONSTRAINT "UQ_0ec8c228e89a95aeb4af9ee3226" UNIQUE (telegram_link_token); + +ALTER TABLE ONLY public.accounts + ADD CONSTRAINT "UQ_3ffa9ce30e56b6d2abf0a465f5f" UNIQUE (telegram_id); + +ALTER TABLE ONLY public.grids + ADD CONSTRAINT "UQ_61f08f04c9f0f1d3afd24b9be0b" UNIQUE (identifier); + +ALTER TABLE ONLY public.poles + ADD CONSTRAINT "UQ_67112e4334d7090a571d2fff42a" UNIQUE (external_reference); + +ALTER TABLE ONLY public.api_keys + ADD CONSTRAINT "UQ_e42cf55faeafdcce01a82d24849" UNIQUE (key); + +ALTER TABLE ONLY public.meter_interactions + ADD CONSTRAINT meter_interactions_order_id_key UNIQUE (order_id); + +ALTER TABLE ONLY public.meter_interactions + ADD CONSTRAINT meter_interactions_pkey PRIMARY KEY (id); + +ALTER TABLE ONLY public.pd_site_submissions + ADD CONSTRAINT pd_public_submissions_pkey PRIMARY KEY (id); + +ALTER TABLE ONLY public.pd_sites + ADD CONSTRAINT pd_sites_pkey PRIMARY KEY (id); + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "FK_04e1a755d5b760f681e5205557d" FOREIGN KEY (dcu_id) REFERENCES public.dcus(id); + +ALTER TABLE ONLY public.mppts + ADD CONSTRAINT "FK_0a681c1d6d0a67331bbc6956427" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "FK_0b03d1bc1cff784570333129e63" FOREIGN KEY (historical_grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.transactions + ADD CONSTRAINT "FK_0b171330be0cb621f8d73b87a9e" FOREIGN KEY (wallet_id) REFERENCES public.wallets(id); + +ALTER TABLE ONLY public.accounts + ADD CONSTRAINT "FK_13bf998d703e7d4c7b9a90f4f76" FOREIGN KEY (supabase_id) REFERENCES auth.users(id); + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_18c521072a6ca9d57365ffb24fe" FOREIGN KEY (agent_id) REFERENCES public.agents(id); + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_236a383b1007cf64f0c8c7f6534" FOREIGN KEY (customer_id) REFERENCES public.customers(id); + +ALTER TABLE ONLY public.accounts + ADD CONSTRAINT "FK_238d61e0f8ac37278f726efac20" FOREIGN KEY (organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.dcus + ADD CONSTRAINT "FK_254c3db39098da62eb3f96f0006" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.poles + ADD CONSTRAINT "FK_2c21d74185234b0dd39588a4d36" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_2d52a7fc85cfbb0421af977766e" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_2fc5f64b328675b5203d54c7929" FOREIGN KEY (connection_id) REFERENCES public.connections(id); + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_336b627b254b70b0702436e6aff" FOREIGN KEY (author_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "FK_35239534a89f0d6dab2612645eb" FOREIGN KEY (author_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY public.notes + ADD CONSTRAINT "FK_35b89a50cb9203dccff44136519" FOREIGN KEY (author_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY public.connections + ADD CONSTRAINT "FK_37f79eb1e29a53cc582fbb805e0" FOREIGN KEY (customer_id) REFERENCES public.customers(id); + +ALTER TABLE ONLY public.ussd_sessions + ADD CONSTRAINT "FK_3a2606011b8d9f1de07de2bf0a8" FOREIGN KEY (meter_id) REFERENCES public.meters(id); + +ALTER TABLE ONLY public.routers + ADD CONSTRAINT "FK_3c3ddc58369a9a8147776e39f40" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.transactions + ADD CONSTRAINT "FK_3cb0558ed36997f1d9ecc1118e7" FOREIGN KEY (order_id) REFERENCES public.orders(id); + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "FK_3f770c153b3333ad1d29cfbf784" FOREIGN KEY (meter_id) REFERENCES public.meters(id); + +ALTER TABLE ONLY public.grids + ADD CONSTRAINT "FK_433ef6a589cd535dff42b34612e" FOREIGN KEY (organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_4a300197465db92edfc5563d20b" FOREIGN KEY (meter_id) REFERENCES public.meters(id); + +ALTER TABLE ONLY public.ussd_sessions + ADD CONSTRAINT "FK_4cad92626a62976ac0b49244d2e" FOREIGN KEY (bank_id) REFERENCES public.banks(id); + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "FK_4eb64f5decb7250cfa993410c1e" FOREIGN KEY (last_meter_commissioning_id) REFERENCES public.meter_commissionings(id) ON DELETE SET NULL; + +ALTER TABLE ONLY public.customers + ADD CONSTRAINT "FK_51a88ee1d4fceb047e7cfda3baa" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "FK_5606d6ec5ab568377509edc5267" FOREIGN KEY (last_encountered_issue_id) REFERENCES public.issues(id) ON DELETE SET NULL; + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "FK_576b0e9af0bec469bff33b965aa" FOREIGN KEY (receiver_wallet_id) REFERENCES public.wallets(id); + +ALTER TABLE ONLY public.metering_hardware_imports + ADD CONSTRAINT "FK_5c2bb31b2e45c3f1da0b72071a2" FOREIGN KEY (metering_hardware_install_session_id) REFERENCES public.metering_hardware_install_sessions(id); + +ALTER TABLE ONLY public.meter_command_batches + ADD CONSTRAINT "FK_5c44fae05dc443720f62664e563" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.notes + ADD CONSTRAINT "FK_5ec9536f852f1923097ba1ecdac" FOREIGN KEY (meter_id) REFERENCES public.meters(id); + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "FK_60816230893327daacc86ab41c8" FOREIGN KEY (author_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "FK_6580899a2293de27787376887fa" FOREIGN KEY (customer_id) REFERENCES public.customers(id); + +ALTER TABLE ONLY public.meter_commissionings + ADD CONSTRAINT "FK_6684f0790c54ed3d441f78df91a" FOREIGN KEY (metering_hardware_install_session_id) REFERENCES public.metering_hardware_install_sessions(id); + +ALTER TABLE ONLY public.meter_command_batches + ADD CONSTRAINT "FK_6e26cb6e966f484f0f897127c84" FOREIGN KEY (author_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY public.meter_command_batch_executions + ADD CONSTRAINT "FK_7b0737005e7c358392d87ebb329" FOREIGN KEY (meter_command_batch_id) REFERENCES public.meter_command_batches(id); + +ALTER TABLE ONLY public.issues + ADD CONSTRAINT "FK_7b15f3fbbedf51a1a3d21583b7e" FOREIGN KEY (meter_id) REFERENCES public.meters(id); + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "FK_86d4557a79e374b5c55cb3b66d8" FOREIGN KEY (last_metering_hardware_install_session_id) REFERENCES public.metering_hardware_install_sessions(id) ON DELETE SET NULL; + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "FK_8db1f4e4f8122bd25d50ad96b26" FOREIGN KEY (meter_id) REFERENCES public.meters(id); + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "FK_8f12fe9c7b078122adcae80375d" FOREIGN KEY (sender_wallet_id) REFERENCES public.wallets(id); + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT "FK_92081072063eaf51300ab6c267d" FOREIGN KEY (ussd_session_id) REFERENCES public.ussd_sessions(id); + +ALTER TABLE ONLY public.dcus + ADD CONSTRAINT "FK_9334b587e6e5291f8ccd1b11c44" FOREIGN KEY (last_metering_hardware_install_session_id) REFERENCES public.metering_hardware_install_sessions(id) ON DELETE SET NULL; + +ALTER TABLE ONLY public.notifications + ADD CONSTRAINT "FK_982f77636cb1da8e76745949530" FOREIGN KEY (notification_parameter_id) REFERENCES public.notification_parameters(id); + +ALTER TABLE ONLY public.agents + ADD CONSTRAINT "FK_9bb7cb7efc780ec4d3dec34354f" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "FK_a11871575f7ddb95e567d842bc6" FOREIGN KEY (pole_id) REFERENCES public.poles(id); + +ALTER TABLE ONLY public.notifications + ADD CONSTRAINT "FK_a1ec3b4b4f2017665b534e60256" FOREIGN KEY (account_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY public.members + ADD CONSTRAINT "FK_a9da2a35fdd2cd46a7bfc57fd73" FOREIGN KEY (busy_commissioning_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_abde505f056e2f46c5df2c12491" FOREIGN KEY (organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.energy_cabins + ADD CONSTRAINT "FK_ba81ffc1506025a549849f06f36" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "FK_be64a09dc738420a409cb960264" FOREIGN KEY (last_metering_hardware_import_id) REFERENCES public.metering_hardware_imports(id) ON DELETE SET NULL; + +ALTER TABLE ONLY public.ussd_session_hops + ADD CONSTRAINT "FK_bf89299bd6534528adad645b7a5" FOREIGN KEY (ussd_session_id) REFERENCES public.ussd_sessions(id); + +ALTER TABLE ONLY public.notifications + ADD CONSTRAINT "FK_c1053eec8005016c7d9febdc484" FOREIGN KEY (grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.notifications + ADD CONSTRAINT "FK_cb7b1fb018b296f2107e998b2ff" FOREIGN KEY (organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT "FK_d281c6e5f391b8de5b221032da6" FOREIGN KEY (connection_id) REFERENCES public.connections(id); + +ALTER TABLE ONLY public.connection_requested_meters + ADD CONSTRAINT "FK_d390197863e9a88be5e122d9695" FOREIGN KEY (connection_id) REFERENCES public.connections(id); + +ALTER TABLE ONLY public.api_keys + ADD CONSTRAINT "FK_decaf331589778e33441b2a8d9e" FOREIGN KEY (account_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY public.agents + ADD CONSTRAINT "FK_df520decc9e003a843d8edd9867" FOREIGN KEY (account_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY public.notes + ADD CONSTRAINT "FK_e04c36c14bc9f01f84cd7655b68" FOREIGN KEY (customer_id) REFERENCES public.customers(id); + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_e3f3409859562fbe10b78d1399e" FOREIGN KEY (member_id) REFERENCES public.members(id); + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "FK_e63e504d8e35ef37a2c56b75eb9" FOREIGN KEY (connection_id) REFERENCES public.connections(id); + +ALTER TABLE ONLY public.audits + ADD CONSTRAINT "FK_eb582acfd9d83af268d328e0b79" FOREIGN KEY (dcu_id) REFERENCES public.dcus(id); + +ALTER TABLE ONLY public.customers + ADD CONSTRAINT "FK_ebcc29963874e55053e8ee80be5" FOREIGN KEY (account_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY public.ussd_sessions + ADD CONSTRAINT "FK_ede50d58fa445bfa012135249fd" FOREIGN KEY (account_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY public.notes + ADD CONSTRAINT "FK_f1a5ea7b77453030e2a1df27479" FOREIGN KEY (connection_id) REFERENCES public.connections(id); + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT "FK_f3c53f4b922a011aa53d9e0b1ad" FOREIGN KEY (dcu_id) REFERENCES public.dcus(id); + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "FK_f499c61c6d6a0ac3f794d966edc" FOREIGN KEY (organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "FK_f5782e05e8688f0cfbb5c4a52ce" FOREIGN KEY (agent_id) REFERENCES public.agents(id); + +ALTER TABLE ONLY public.members + ADD CONSTRAINT "FK_fd9dfb97e21b75fc45d42aa614a" FOREIGN KEY (account_id) REFERENCES public.accounts(id); + +ALTER TABLE ONLY public.agents + ADD CONSTRAINT agents_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.connection_requested_meters + ADD CONSTRAINT connection_requested_meters_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.connections + ADD CONSTRAINT connections_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.customers + ADD CONSTRAINT customers_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.dcus + ADD CONSTRAINT dcus_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.meter_command_batch_executions + ADD CONSTRAINT meter_command_batch_executions_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.meter_command_batches + ADD CONSTRAINT meter_command_batches_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.energy_cabins + ADD CONSTRAINT energy_cabins_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.issues + ADD CONSTRAINT issues_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.members + ADD CONSTRAINT members_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.meter_commissionings + ADD CONSTRAINT meter_commissionings_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.meter_interactions + ADD CONSTRAINT meter_interactions_batch_execution_id_fkey FOREIGN KEY (batch_execution_id) REFERENCES public.meter_command_batch_executions(id); + +ALTER TABLE ONLY public.meter_interactions + ADD CONSTRAINT meter_interactions_meter_commissioning_id_fkey FOREIGN KEY (meter_commissioning_id) REFERENCES public.meter_commissionings(id); + +ALTER TABLE ONLY public.meter_interactions + ADD CONSTRAINT meter_interactions_meter_id_fkey FOREIGN KEY (meter_id) REFERENCES public.meters(id); + +ALTER TABLE ONLY public.meter_interactions + ADD CONSTRAINT meter_interactions_order_id_fkey FOREIGN KEY (order_id) REFERENCES public.orders(id); + +ALTER TABLE ONLY public.metering_hardware_imports + ADD CONSTRAINT metering_hardware_imports_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.metering_hardware_install_sessions + ADD CONSTRAINT metering_hardware_install_sessions_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT meters_rls_grid_id_fkey FOREIGN KEY (rls_grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.meters + ADD CONSTRAINT meters_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.mppts + ADD CONSTRAINT mppts_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.notes + ADD CONSTRAINT notes_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.orders + ADD CONSTRAINT orders_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.pd_site_submissions + ADD CONSTRAINT pd_site_submissions_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.pd_sites + ADD CONSTRAINT pd_sites_operations_grid_id_fkey FOREIGN KEY (operations_grid_id) REFERENCES public.grids(id); + +ALTER TABLE ONLY public.pd_sites + ADD CONSTRAINT pd_sites_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.poles + ADD CONSTRAINT poles_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.routers + ADD CONSTRAINT routers_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.transactions + ADD CONSTRAINT transactions_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT wallets_rls_organization_id_fkey FOREIGN KEY (rls_organization_id) REFERENCES public.organizations(id); + +-- ============================================================================= +-- Indexes +-- ============================================================================= +-- Existing kept indexes (renamed where their table was renamed), followed by +-- 49 new indexes: register #22 (partial unique on organizations), plus 48 +-- FK/RLS-predicate coverage gaps from the schema audit (register #25, #30). + +CREATE INDEX "IDX_14f6e0badc019bbdd2f66f7e8a" ON public.notifications USING btree (notification_status); + +CREATE UNIQUE INDEX "IDX_36d092cd2d89756f62421011bb" ON public.meters USING btree (external_reference, external_system); + +CREATE INDEX "IDX_5c820aa56824815f8e19484ff5" ON public.transactions USING btree (wallet_id, transaction_status, created_at); + +CREATE UNIQUE INDEX "IDX_9c4f5693f52e6e5e51e1ca05af" ON public.dcus USING btree (external_reference, external_system); + +CREATE INDEX idx_agents_rls_organization_id ON public.agents USING btree (rls_organization_id); + +CREATE INDEX idx_connection_customer_id ON public.connections USING btree (customer_id); + +CREATE INDEX idx_connection_requested_meters_rls_organization_id ON public.connection_requested_meters USING btree (rls_organization_id); + +CREATE INDEX idx_connections_rls_organization_id ON public.connections USING btree (rls_organization_id); + +CREATE INDEX idx_customer_grid_id ON public.customers USING btree (grid_id); + +CREATE INDEX idx_customers_rls_organization_id ON public.customers USING btree (rls_organization_id); + +CREATE INDEX idx_dcus_rls_organization_id ON public.dcus USING btree (rls_organization_id); + +CREATE INDEX idx_meter_command_batch_executions_rls_organization_id ON public.meter_command_batch_executions USING btree (rls_organization_id); + +CREATE INDEX idx_meter_command_batches_rls_organization_id ON public.meter_command_batches USING btree (rls_organization_id); + +CREATE INDEX idx_energy_cabins_location_geom ON public.energy_cabins USING gist (location_geom); + +CREATE INDEX idx_energy_cabins_rls_organization_id ON public.energy_cabins USING btree (rls_organization_id); + +CREATE INDEX idx_issues_open ON public.issues USING btree (rls_organization_id, meter_id, created_at DESC) WHERE (issue_status = 'OPEN'::public.issue_status_enum); + +CREATE INDEX idx_issues_rls_organization_id ON public.issues USING btree (rls_organization_id); + +CREATE INDEX idx_members_rls_organization_id ON public.members USING btree (rls_organization_id); + +CREATE INDEX idx_meter_commissionings_rls_organization_id ON public.meter_commissionings USING btree (rls_organization_id); + +CREATE INDEX idx_meter_connection_id ON public.meters USING btree (connection_id); + +CREATE INDEX idx_meter_dcu_id ON public.meters USING btree (dcu_id); + +CREATE INDEX idx_meter_interaction_batch_execution_id ON public.meter_interactions USING btree (batch_execution_id, meter_interaction_status); + +CREATE INDEX idx_meter_interaction_meter_commissioning_id ON public.meter_interactions USING btree (meter_commissioning_id); + +CREATE INDEX idx_meter_interaction_meter_id_extra ON public.meter_interactions USING btree (meter_id, meter_interaction_type, created_at DESC); + +CREATE INDEX idx_metering_hardware_install_sessions_rls_organization_id ON public.metering_hardware_install_sessions USING btree (rls_organization_id); + +CREATE INDEX idx_meters_rls_organization_id ON public.meters USING btree (rls_organization_id); + +CREATE INDEX idx_mppts_rls_organization_id ON public.mppts USING btree (rls_organization_id); + +CREATE INDEX idx_notes_rls_organization_id ON public.notes USING btree (rls_organization_id); + +CREATE INDEX idx_orders_energy_topup_grid ON public.orders USING btree (historical_grid_id) WHERE (meta_order_type = 'ENERGY_TOPUP'::public.order_type_enum); + +CREATE INDEX idx_orders_energy_topup_receiver ON public.orders USING btree (meta_receiver_id) WHERE (meta_order_type = 'ENERGY_TOPUP'::public.order_type_enum); + +CREATE INDEX idx_orders_historical_grid_id ON public.orders USING btree (historical_grid_id); + +CREATE INDEX idx_orders_meta_receiver_id ON public.orders USING btree (meta_receiver_id); + +CREATE INDEX idx_orders_order_optimized ON public.orders USING btree (updated_at DESC); + +CREATE INDEX idx_orders_rls_organization_id ON public.orders USING btree (rls_organization_id); + +CREATE INDEX idx_orders_rls_sender_receiver ON public.orders USING btree (meta_sender_type, meta_sender_id, meta_receiver_type, meta_receiver_id); + +CREATE INDEX idx_pd_site_submissions_location_geom ON public.pd_site_submissions USING gist (location_geom); + +CREATE INDEX idx_pd_site_submissions_outline_geom ON public.pd_site_submissions USING gist (outline_geom); + +CREATE INDEX idx_pd_sites_location_geom ON public.pd_sites USING gist (location_geom); + +CREATE INDEX idx_pd_sites_outline_geom ON public.pd_sites USING gist (outline_geom); + +CREATE INDEX idx_poles_location_geom ON public.poles USING gist (location_geom); + +CREATE INDEX idx_poles_rls_organization_id ON public.poles USING btree (rls_organization_id); + +CREATE INDEX idx_routers_rls_organization_id ON public.routers USING btree (rls_organization_id); + +CREATE INDEX idx_transactions_rls_organization_id ON public.transactions USING btree (rls_organization_id); + +CREATE INDEX idx_wallets_rls_organization_id ON public.wallets USING btree (rls_organization_id); + +-- New indexes (register #25, #30) — FK/RLS-predicate coverage gaps found while +-- auditing the legacy schema; not present in the legacy reference DB. + +-- register #22 — at most one platform-operator organization (ADR-007 Amendment) +CREATE UNIQUE INDEX one_platform_operator_org ON public.organizations USING btree (organization_type) WHERE (organization_type = 'PLATFORM_OPERATOR'::public.organization_type_enum); + +-- register #25 (Task 3c H1b) +CREATE INDEX idx_accounts_organization_id ON public.accounts USING btree (organization_id); + +-- register #30 Tier 1 — RLS-predicate / renamed-FK gaps +CREATE INDEX idx_grids_organization_id ON public.grids USING btree (organization_id); +CREATE INDEX idx_metering_hardware_imports_rls_organization_id ON public.metering_hardware_imports USING btree (rls_organization_id); +CREATE INDEX idx_meter_command_batch_executions_meter_command_batch_id ON public.meter_command_batch_executions USING btree (meter_command_batch_id); + +-- register #30 Tier 2 — plain FK-column hygiene (44 columns across 22 tables) +CREATE INDEX idx_agents_grid_id ON public.agents USING btree (grid_id); +CREATE INDEX idx_api_keys_account_id ON public.api_keys USING btree (account_id); +CREATE INDEX idx_audits_agent_id ON public.audits USING btree (agent_id); +CREATE INDEX idx_audits_author_id ON public.audits USING btree (author_id); +CREATE INDEX idx_audits_connection_id ON public.audits USING btree (connection_id); +CREATE INDEX idx_audits_customer_id ON public.audits USING btree (customer_id); +CREATE INDEX idx_audits_dcu_id ON public.audits USING btree (dcu_id); +CREATE INDEX idx_audits_grid_id ON public.audits USING btree (grid_id); +CREATE INDEX idx_audits_member_id ON public.audits USING btree (member_id); +CREATE INDEX idx_audits_meter_id ON public.audits USING btree (meter_id); +CREATE INDEX idx_audits_organization_id ON public.audits USING btree (organization_id); +CREATE INDEX idx_connection_requested_meters_connection_id ON public.connection_requested_meters USING btree (connection_id); +CREATE INDEX idx_dcus_grid_id ON public.dcus USING btree (grid_id); +CREATE INDEX idx_energy_cabins_grid_id ON public.energy_cabins USING btree (grid_id); +CREATE INDEX idx_meter_command_batches_author_id ON public.meter_command_batches USING btree (author_id); +CREATE INDEX idx_meter_command_batches_grid_id ON public.meter_command_batches USING btree (grid_id); +CREATE INDEX idx_meter_commissionings_metering_hardware_install_session_id ON public.meter_commissionings USING btree (metering_hardware_install_session_id); +CREATE INDEX idx_metering_hardware_imports_metering_hardware_install_session_id ON public.metering_hardware_imports USING btree (metering_hardware_install_session_id); +CREATE INDEX idx_metering_hardware_install_sessions_author_id ON public.metering_hardware_install_sessions USING btree (author_id); +CREATE INDEX idx_metering_hardware_install_sessions_dcu_id ON public.metering_hardware_install_sessions USING btree (dcu_id); +CREATE INDEX idx_metering_hardware_install_sessions_meter_id ON public.metering_hardware_install_sessions USING btree (meter_id); +CREATE INDEX idx_meters_pole_id ON public.meters USING btree (pole_id); +CREATE INDEX idx_mppts_grid_id ON public.mppts USING btree (grid_id); +CREATE INDEX idx_notes_author_id ON public.notes USING btree (author_id); +CREATE INDEX idx_notes_connection_id ON public.notes USING btree (connection_id); +CREATE INDEX idx_notes_customer_id ON public.notes USING btree (customer_id); +CREATE INDEX idx_notes_meter_id ON public.notes USING btree (meter_id); +CREATE INDEX idx_notifications_account_id ON public.notifications USING btree (account_id); +CREATE INDEX idx_notifications_grid_id ON public.notifications USING btree (grid_id); +CREATE INDEX idx_notifications_notification_parameter_id ON public.notifications USING btree (notification_parameter_id); +CREATE INDEX idx_notifications_organization_id ON public.notifications USING btree (organization_id); +CREATE INDEX idx_orders_author_id ON public.orders USING btree (author_id); +CREATE INDEX idx_orders_receiver_wallet_id ON public.orders USING btree (receiver_wallet_id); +CREATE INDEX idx_orders_sender_wallet_id ON public.orders USING btree (sender_wallet_id); +CREATE INDEX idx_pd_site_submissions_organization_id ON public.pd_site_submissions USING btree (organization_id); +CREATE INDEX idx_pd_sites_operations_grid_id ON public.pd_sites USING btree (operations_grid_id); +CREATE INDEX idx_pd_sites_organization_id ON public.pd_sites USING btree (organization_id); +CREATE INDEX idx_poles_grid_id ON public.poles USING btree (grid_id); +CREATE INDEX idx_routers_grid_id ON public.routers USING btree (grid_id); +CREATE INDEX idx_transactions_order_id ON public.transactions USING btree (order_id); +CREATE INDEX idx_ussd_session_hops_ussd_session_id ON public.ussd_session_hops USING btree (ussd_session_id); +CREATE INDEX idx_ussd_sessions_account_id ON public.ussd_sessions USING btree (account_id); +CREATE INDEX idx_ussd_sessions_bank_id ON public.ussd_sessions USING btree (bank_id); +CREATE INDEX idx_ussd_sessions_meter_id ON public.ussd_sessions USING btree (meter_id); + +-- ============================================================================= +-- Functions +-- ============================================================================= +-- 25 keep functions: 17 carried over from the legacy chain (10 of them with +-- redesigned bodies per register #23), 2 renamed/redesigned (register #16, +-- #22), and 6 new RLS org-lookup leaf/delegate helpers (register #23). +-- Dropped: append_rls_organization_id_by_device_id, +-- append_rls_organization_id_by_receiver_meter_id, +-- append_rls_organization_id_by_historical_grid_id (register #12, #7, #21), +-- lock_next_order, lock_next_pd_action (register #13, #14), +-- notify_make_about_is_fs_on_updated, notify_make_about_is_hps_on_updated, +-- notify_make_about_kwh_tariff_essential_service_updated (register #6, +-- parameterized to the Make-integration operator recipe). + +-- --------------------------------------------------------------------------- +-- RLS org-lookup helpers (register #23 §4) — leaf + delegates, STABLE +-- --------------------------------------------------------------------------- + +CREATE FUNCTION public.rls_org_id_from_grid(grid_id integer) RETURNS integer + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO '' + AS $$ + SELECT organization_id FROM public.grids WHERE id = grid_id; +$$; + +CREATE FUNCTION public.rls_org_id_from_customer(customer_id integer) RETURNS integer + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO '' + AS $$ + SELECT public.rls_org_id_from_grid(grid_id) FROM public.customers WHERE id = customer_id; +$$; + +CREATE FUNCTION public.rls_org_id_from_connection(connection_id integer) RETURNS integer + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO '' + AS $$ + SELECT public.rls_org_id_from_customer(customer_id) FROM public.connections WHERE id = connection_id; +$$; + +CREATE FUNCTION public.rls_org_id_from_agent(agent_id integer) RETURNS integer + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO '' + AS $$ + SELECT public.rls_org_id_from_grid(grid_id) FROM public.agents WHERE id = agent_id; +$$; + +CREATE FUNCTION public.rls_org_id_from_meter(meter_id integer) RETURNS integer + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO '' + AS $$ + SELECT public.rls_org_id_from_connection(connection_id) FROM public.meters WHERE id = meter_id; +$$; + +CREATE FUNCTION public.rls_org_id_from_dcu(dcu_id integer) RETURNS integer + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO '' + AS $$ + SELECT public.rls_org_id_from_grid(grid_id) FROM public.dcus WHERE id = dcu_id; +$$; + +-- --------------------------------------------------------------------------- +-- append_rls_organization_id_by_* trigger functions (register #23 §4) — +-- redesigned to delegate to the helpers above; dead joins removed; +-- SET search_path TO '' added throughout +-- --------------------------------------------------------------------------- + +CREATE FUNCTION public.append_rls_organization_id_by_account_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + SELECT + organization_id INTO NEW.rls_organization_id + FROM + public.accounts + WHERE + accounts.id = NEW.account_id; + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.append_rls_organization_id_by_grid_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + NEW.rls_organization_id := public.rls_org_id_from_grid(NEW.grid_id); + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.append_rls_organization_id_by_connection_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + NEW.rls_organization_id := public.rls_org_id_from_connection(NEW.connection_id); + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.append_rls_organization_id_by_customer_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + NEW.rls_organization_id := public.rls_org_id_from_customer(NEW.customer_id); + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.append_rls_organization_id_by_customer_id_or_agent_id_or_connec() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + IF NEW.customer_id IS NOT NULL THEN + NEW.rls_organization_id := public.rls_org_id_from_customer(NEW.customer_id); + ELSIF NEW.agent_id IS NOT NULL THEN + NEW.rls_organization_id := public.rls_org_id_from_agent(NEW.agent_id); + ELSIF NEW.connection_id IS NOT NULL THEN + NEW.rls_organization_id := public.rls_org_id_from_connection(NEW.connection_id); + ELSIF NEW.organization_id IS NOT NULL THEN + NEW.rls_organization_id := NEW.organization_id; + ELSIF NEW.meter_id IS NOT NULL THEN + NEW.rls_organization_id := public.rls_org_id_from_meter(NEW.meter_id); + END IF; + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.append_rls_organization_id_by_dcu_id_or_meter_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + IF NEW.dcu_id IS NOT NULL THEN + NEW.rls_organization_id := public.rls_org_id_from_dcu(NEW.dcu_id); + ELSIF NEW.meter_id IS NOT NULL THEN + NEW.rls_organization_id := public.rls_org_id_from_meter(NEW.meter_id); + END IF; + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.append_rls_organization_id_by_meter_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + NEW.rls_organization_id := public.rls_org_id_from_meter(NEW.meter_id); + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.append_rls_organization_id_by_metering_hardware_install_session() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +DECLARE + session_meter_id integer; +BEGIN + SELECT + meter_id INTO session_meter_id + FROM + public.metering_hardware_install_sessions + WHERE + id = NEW.metering_hardware_install_session_id; + NEW.rls_organization_id := public.rls_org_id_from_meter(session_meter_id); + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.append_rls_organization_id_by_order_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +DECLARE + order_grid_id integer; +BEGIN + SELECT + historical_grid_id INTO order_grid_id + FROM + public.orders + WHERE + id = NEW.order_id; + NEW.rls_organization_id := public.rls_org_id_from_grid(order_grid_id); + RETURN NEW; +END; +$$; + +-- Renamed from append_rls_organization_id_by_directive_batch_id() (register #16); body redesigned (register #23) +CREATE FUNCTION public.append_rls_organization_id_by_meter_command_batch_id() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +DECLARE + batch_grid_id integer; +BEGIN + SELECT + grid_id INTO batch_grid_id + FROM + public.meter_command_batches + WHERE + id = NEW.meter_command_batch_id; + NEW.rls_organization_id := public.rls_org_id_from_grid(batch_grid_id); + RETURN NEW; +END; +$$; + +-- --------------------------------------------------------------------------- +-- Admin-organization membership check (register #22) — renamed from +-- rls_check_if_nxt_member(); reads PLATFORM_OPERATOR row directly (indexed +-- via one_platform_operator_org). GUC + ALTER DATABASE dropped: Supabase +-- migrations run as non-superuser postgres and cannot set custom DB params. +-- --------------------------------------------------------------------------- + +CREATE FUNCTION public.rls_check_if_admin_org_member() RETURNS boolean + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO '' + AS $$ + SELECT (auth.jwt () -> 'app_metadata' ->> 'account_type') = 'MEMBER' + AND (auth.jwt () -> 'app_metadata' ->> 'organization_id')::int = ( + SELECT + o.id + FROM + public.organizations AS o + WHERE + o.organization_type = 'PLATFORM_OPERATOR' + ORDER BY + o.id + LIMIT 1); +$$; + +-- --------------------------------------------------------------------------- +-- Remaining RLS helpers — mark STABLE only (register #31), logic unchanged +-- --------------------------------------------------------------------------- + +CREATE FUNCTION public.rls_check_if_lender() RETURNS boolean + LANGUAGE plpgsql STABLE SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + RETURN (auth.jwt () -> 'app_metadata' ->> 'organization_type')::text = 'LENDER'; +END; +$$; + +CREATE FUNCTION public.rls_get_member_org_id() RETURNS integer + LANGUAGE plpgsql STABLE SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + RETURN (auth.jwt () -> 'app_metadata' ->> 'organization_id')::int; +END; +$$; + +-- --------------------------------------------------------------------------- +-- auth.users trigger functions (H2 keep, unchanged) +-- --------------------------------------------------------------------------- + +CREATE FUNCTION public.handle_new_user() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + -- RAISE LOG 'New user: %', NEW; + -- Every Supabase User gets an Account that holds more metadata + -- We don't have access to raw_app_meta_data here but we do in + -- the subsequent update trigger + INSERT INTO public.accounts (supabase_id, email, phone, full_name, telegram_link_token) + VALUES(NEW.id, NEW.email, NEW.phone, NEW.raw_user_meta_data ->> 'full_name', gen_random_uuid ()); + RETURN NEW; +END; +$$; + +CREATE FUNCTION public.handle_update_user() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $$ +BEGIN + UPDATE + public.accounts + SET + email = NEW.email, + phone = NEW.phone, + full_name = NEW.raw_user_meta_data ->> 'full_name', + organization_id = CAST(COALESCE(NEW.raw_app_meta_data ->> 'organization_id', NULL) AS integer) + WHERE + supabase_id = NEW.id; + RETURN NEW; +END; +$$; + +-- --------------------------------------------------------------------------- +-- Payments / production-monitoring RPCs +-- --------------------------------------------------------------------------- + +-- register #24: SET search_path TO '' added; logic unchanged +CREATE FUNCTION public.lock_next_order_and_wallets(uuid uuid) RETURNS TABLE(id integer) + LANGUAGE plpgsql + SET search_path TO '' + AS $$ +DECLARE + order_id_var integer; + sender_wallet_id_var integer; + receiver_wallet_id_var integer; +BEGIN + -- 1. Atomic Selection + SELECT + _orders.id, + _orders.sender_wallet_id, + _orders.receiver_wallet_id + INTO + order_id_var, + sender_wallet_id_var, + receiver_wallet_id_var + FROM + public.orders _orders + JOIN public.wallets AS sender_wallets ON sender_wallets.id = _orders.sender_wallet_id + JOIN public.wallets AS receiver_wallets ON receiver_wallets.id = _orders.receiver_wallet_id + WHERE + _orders.order_status = 'PENDING' + AND sender_wallets.lock_session IS NULL + AND receiver_wallets.lock_session IS NULL + ORDER BY + _orders.id ASC + LIMIT 1 + FOR UPDATE OF _orders, sender_wallets, receiver_wallets SKIP LOCKED; + + -- 2. Early exit if no order matches + IF order_id_var IS NULL THEN + RETURN; + END IF; + + -- 3. Update the Order + -- explicit alias 'order_record' prevents confusion with any variables + UPDATE + public.orders AS order_record + SET + lock_session = uuid + WHERE + order_record.id = order_id_var; + + -- 4. Update the Wallets and Return their IDs + -- explicit alias 'wallet_record' prevents confusion + RETURN QUERY + UPDATE + public.wallets AS wallet_record + SET + lock_session = uuid + WHERE + wallet_record.id IN (sender_wallet_id_var, receiver_wallet_id_var) + RETURNING + wallet_record.id; +END; +$$; + +-- register #27: mark STABLE; logic unchanged +CREATE FUNCTION public.find_energy_topup_revenue(grid_id integer, start_date timestamp with time zone, end_date timestamp with time zone) RETURNS numeric + LANGUAGE plpgsql STABLE + SET search_path TO '' + AS $$ +DECLARE + total_revenue NUMERIC; +BEGIN + SELECT + SUM(amount) INTO total_revenue + FROM + public.orders + WHERE + meta_order_type = 'ENERGY_TOPUP'::public.order_type_enum + AND order_status = 'COMPLETED'::public.order_status_enum + AND meta_is_hidden_from_reporting = FALSE + AND historical_grid_id = grid_id + AND created_at >= start_date + AND created_at < end_date; + RETURN total_revenue; +END; +$$; + +-- register #27: mark STABLE; GROUP BY meta_receiver_id dropped (customer-level +-- top-spender aggregation; the legacy meter id in GROUP BY split one customer +-- across rows) — return shape unchanged +CREATE FUNCTION public.find_top_spenders(grid_id integer, limit_count integer, start_date timestamp with time zone, end_date timestamp with time zone) RETURNS TABLE(full_name character varying, id integer, amount double precision) + LANGUAGE plpgsql STABLE + SET search_path TO '' + AS $$ +BEGIN + RETURN QUERY + SELECT + meta_receiver_name_part_2 AS full_name, + meta_receiver_id_part_2 AS id, + SUM( + _orders.amount +) AS amount + FROM + public.orders AS _orders + WHERE + meta_order_type = 'ENERGY_TOPUP'::public.order_type_enum + AND order_status = 'COMPLETED'::public.order_status_enum + AND meta_is_hidden_from_reporting = FALSE + AND historical_grid_id = grid_id + AND created_at >= start_date + AND created_at < end_date + GROUP BY + meta_receiver_id_part_2, + meta_receiver_name_part_2 + ORDER BY + SUM( + _orders.amount +) DESC + LIMIT limit_count; +END; +$$; + +-- register #26: are_all_dcus_online / are_all_dcus_under_high_load_threshold +-- dropped from RETURNS TABLE and SELECT body (grids columns dropped, register +-- #17); mark STABLE. Pegasus gateway alerts use dcus.is_online instead. +CREATE FUNCTION public.get_grid_status(grid_id integer) RETURNS TABLE(is_hps_on boolean, is_fs_on boolean, is_cabin_meter_credit_depleting boolean, customer_count integer) + LANGUAGE plpgsql STABLE + SET search_path TO '' + AS $$BEGIN + RETURN QUERY + SELECT + _grid.is_hps_on, + _grid.is_fs_on, + _grid.is_cabin_meter_credit_depleting, + ( + SELECT + COUNT(DISTINCT _customer.id)::integer + FROM + public.customers _customer + JOIN public.accounts _account ON _account.id = _customer.account_id + JOIN public.connections _connection ON _connection.customer_id = _customer.id + JOIN public.meters _meter ON _meter.connection_id = _connection.id + WHERE + _customer.grid_id = _grid.id + AND _customer.is_hidden_from_reporting = FALSE + AND _account.deleted_at IS NULL) AS customer_count + FROM + public.grids _grid + WHERE + _grid.id = grid_id; +END;$$; + +-- ============================================================================= +-- Triggers +-- ============================================================================= +-- 21 triggers: 19 append_rls_organization_id_* triggers backing denormalized +-- RLS columns (3 renamed alongside their table, register #16) and the 2 +-- auth.users triggers carried over from the legacy chain unchanged. + +CREATE TRIGGER append_rls_organization_id_on_agent_insert BEFORE INSERT ON public.agents FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + +CREATE TRIGGER append_rls_organization_id_on_connection_insert BEFORE INSERT ON public.connections FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_customer_id(); + +CREATE TRIGGER append_rls_organization_id_on_customer_insert BEFORE INSERT ON public.customers FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + +CREATE TRIGGER append_rls_organization_id_on_dcus_insert BEFORE INSERT ON public.dcus FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + +CREATE TRIGGER append_rls_organization_id_on_meter_command_batch_execution_insert BEFORE INSERT ON public.meter_command_batch_executions FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_meter_command_batch_id(); + +CREATE TRIGGER append_rls_organization_id_on_meter_command_batch_insert BEFORE INSERT ON public.meter_command_batches FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + +CREATE TRIGGER append_rls_organization_id_on_energy_cabin_insert BEFORE INSERT ON public.energy_cabins FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + +CREATE TRIGGER append_rls_organization_id_on_issue_insert BEFORE INSERT ON public.issues FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_meter_id(); + +CREATE TRIGGER append_rls_organization_id_on_member_insert BEFORE INSERT ON public.members FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_account_id(); + +CREATE TRIGGER append_rls_organization_id_on_meter_commissioning_insert BEFORE INSERT ON public.meter_commissionings FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_metering_hardware_install_session(); + +CREATE TRIGGER append_rls_organization_id_on_meter_insert BEFORE INSERT ON public.meters FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_connection_id(); + +CREATE TRIGGER append_rls_organization_id_on_meter_install_session_insert BEFORE INSERT ON public.metering_hardware_install_sessions FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_dcu_id_or_meter_id(); + +CREATE TRIGGER append_rls_organization_id_on_mppt_insert BEFORE INSERT ON public.mppts FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + +CREATE TRIGGER append_rls_organization_id_on_note_insert BEFORE INSERT ON public.notes FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_customer_id(); + +CREATE TRIGGER append_rls_organization_id_on_pole_insert BEFORE INSERT ON public.poles FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + +CREATE TRIGGER append_rls_organization_id_on_requested_connection_meters_inser BEFORE INSERT ON public.connection_requested_meters FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_connection_id(); + +CREATE TRIGGER append_rls_organization_id_on_router_insert BEFORE INSERT ON public.routers FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_grid_id(); + +CREATE TRIGGER append_rls_organization_id_on_transaction_insert BEFORE INSERT ON public.transactions FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_order_id(); + +CREATE TRIGGER append_rls_organization_id_on_wallet_insert BEFORE INSERT ON public.wallets FOR EACH ROW EXECUTE FUNCTION public.append_rls_organization_id_by_customer_id_or_agent_id_or_connec(); + +CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION public.handle_new_user(); + +CREATE TRIGGER on_auth_user_updated AFTER UPDATE ON auth.users FOR EACH ROW EXECUTE FUNCTION public.handle_update_user(); + +-- ============================================================================= +-- Row-level security +-- ============================================================================= +-- RLS is enabled on all 35 kept tables (unchanged from the legacy chain). +-- 76 of the legacy chain's 123 policies are kept — 26 dropped with their +-- table, 21 dropped for targeting company-infra readonly roles +-- (grafana_readonly, make_readonly, snaplet_readonly_2) that this baseline +-- does not provision. 18 policies are rewritten to wrap their RLS helper +-- call in `( SELECT … )` for per-statement (not per-row) evaluation (register +-- #32) — the other 51 helper-calling policies were already wrapped upstream. + +ALTER TABLE public.accounts ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.agents ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.api_keys ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.audits ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.banks ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.connection_requested_meters ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.connections ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.customers ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.dcus ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.meter_command_batch_executions ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.meter_command_batches ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.energy_cabins ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.grids ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.issues ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.members ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.meter_commissionings ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.meter_interactions ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.metering_hardware_imports ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.metering_hardware_install_sessions ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.meters ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.mppts ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.notes ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.notification_parameters ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.notifications ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.orders ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.organizations ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.pd_site_submissions ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.pd_sites ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.poles ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.routers ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.solcast_cache ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.transactions ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.ussd_session_hops ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.ussd_sessions ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.wallets ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "Allow NXT Grid to CRUD" ON public.connections TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to insert" ON public.grids FOR INSERT TO authenticated WITH CHECK (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to insert" ON public.meters FOR INSERT TO authenticated WITH CHECK (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to insert" ON public.notes FOR INSERT TO authenticated WITH CHECK (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to insert" ON public.organizations FOR INSERT TO authenticated WITH CHECK (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to insert" ON public.pd_sites FOR INSERT TO authenticated WITH CHECK (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to insert" ON public.poles FOR INSERT TO authenticated WITH CHECK (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to insert" ON public.wallets FOR INSERT WITH CHECK (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.accounts FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.agents FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.connection_requested_meters FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.connections FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.customers FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.dcus FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.meter_command_batch_executions FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.meter_command_batches FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.energy_cabins FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.grids FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.issues FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.members FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.meter_commissionings FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.meter_interactions FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.metering_hardware_install_sessions FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.meters FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.mppts FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.notes FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.orders FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.organizations FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.pd_sites FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.poles FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.routers FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.transactions FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to select" ON public.wallets FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to update" ON public.accounts FOR UPDATE TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to update" ON public.connections FOR UPDATE TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to update" ON public.grids FOR UPDATE TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to update" ON public.meters FOR UPDATE TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to update" ON public.organizations FOR UPDATE TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to update" ON public.pd_site_submissions FOR UPDATE TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow NXT Grid to update" ON public.pd_sites FOR UPDATE TO authenticated USING (( SELECT public.rls_check_if_admin_org_member() AS rls_check_if_admin_org_member)); + +CREATE POLICY "Allow authenticated to do everything" ON public.audits USING (true); + +CREATE POLICY "Allow authenticated to select" ON public.metering_hardware_imports FOR SELECT TO authenticated USING (true); + +CREATE POLICY "Allow authors to update their own" ON public.notes FOR UPDATE TO authenticated USING ((( SELECT auth.uid() AS uid) = ( SELECT account.supabase_id + FROM public.accounts account + WHERE (account.id = notes.author_id)))); + +CREATE POLICY "Allow lenders to select" ON public.accounts FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_lender() AS rls_check_if_lender)); + +CREATE POLICY "Allow lenders to select" ON public.customers FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_lender() AS rls_check_if_lender)); + +CREATE POLICY "Allow lenders to select" ON public.grids FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_lender() AS rls_check_if_lender)); + +CREATE POLICY "Allow lenders to select" ON public.orders FOR SELECT TO authenticated USING (( SELECT public.rls_check_if_lender() AS rls_check_if_lender)); + +CREATE POLICY "Allow org member to select" ON public.accounts FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = organization_id)); + +CREATE POLICY "Allow org members to insert" ON public.notes FOR INSERT TO authenticated WITH CHECK (( SELECT (public.rls_get_member_org_id() = rls_organization_id) AS rls_get_member_org_id)); + +CREATE POLICY "Allow org members to insert" ON public.poles FOR INSERT WITH CHECK (( SELECT (public.rls_get_member_org_id() = rls_organization_id) AS rls_get_member_org_id)); + +CREATE POLICY "Allow org members to select" ON public.agents FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.connection_requested_meters FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.connections FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.customers FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.dcus FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.meter_command_batches FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.energy_cabins FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.grids FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = organization_id)); + +CREATE POLICY "Allow org members to select" ON public.issues FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.members FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.meter_commissionings FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.meter_interactions FOR SELECT TO authenticated USING (true); + +CREATE POLICY "Allow org members to select" ON public.metering_hardware_install_sessions FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.mppts FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.notes FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.orders FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.organizations FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = id)); + +CREATE POLICY "Allow org members to select" ON public.poles FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.routers FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select" ON public.transactions FOR SELECT TO authenticated USING ((( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id)); + +CREATE POLICY "Allow org members to select (TEMPORARILY OPEN)" ON public.wallets FOR SELECT TO authenticated USING (true); + +CREATE POLICY "Allow org members to select own and UNASSIGNED" ON public.meters FOR SELECT TO authenticated USING (((rls_organization_id IS NULL) OR (( SELECT public.rls_get_member_org_id() AS rls_get_member_org_id) = rls_organization_id))); + +CREATE POLICY "Allow org members to update" ON public.accounts FOR UPDATE TO authenticated USING (( SELECT (public.rls_get_member_org_id() = organization_id) AS rls_get_member_org_id)); + +CREATE POLICY "Allow org members to update" ON public.orders FOR UPDATE TO authenticated USING (( SELECT (public.rls_get_member_org_id() = rls_organization_id) AS rls_get_member_org_id)); + +CREATE POLICY "Allow public selects (temporary for sending response to MAKE)" ON public.pd_site_submissions FOR SELECT USING (true); + +CREATE POLICY "Allow public writes (Sites can be submitted publicly)" ON public.pd_site_submissions FOR INSERT WITH CHECK (true); + +-- ============================================================================= +-- Grants (Data API access) +-- ============================================================================= +-- Explicit per-object GRANTs for every kept table, sequence, and function, to +-- anon/authenticated/service_role (register #33) — required for Data API +-- (PostgREST/GraphQL) reachability; Supabase stopped auto-granting these on +-- new projects from 2026-05-30. The corresponding 3 ALTER DEFAULT PRIVILEGES +-- statements are deliberately omitted: future tables get explicit grants per +-- migration instead of an ambient auto-expose default. + +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 SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.accounts TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.accounts TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.accounts TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.agents TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.agents TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.agents TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.agents_with_account TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.agents_with_account TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.agents_with_account TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.api_keys TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.api_keys TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.api_keys TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.audits TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.audits TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.audits TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.banks TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.banks TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.banks TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.dcus TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.dcus TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.dcus TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meters TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meters TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meters TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.poles TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.poles TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.poles TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.connection_requested_meters TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.connection_requested_meters TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.connection_requested_meters TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.connections TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.connections TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.connections TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.customers TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.customers TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.customers TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.customers_with_account TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.customers_with_account TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.customers_with_account TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_command_batch_executions TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_command_batch_executions TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_command_batch_executions TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_command_batches TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_command_batches TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_command_batches TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.energy_cabins TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.energy_cabins TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.energy_cabins TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.grids TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.grids TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.grids TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.issues TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.issues TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.issues TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.members TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.members TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.members TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_commissionings TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_commissionings TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_commissionings TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_interactions TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_interactions TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meter_interactions TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.metering_hardware_imports TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.metering_hardware_imports TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.metering_hardware_imports TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.metering_hardware_install_sessions TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.metering_hardware_install_sessions TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.metering_hardware_install_sessions TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meters_with_account_and_statuses TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meters_with_account_and_statuses TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.meters_with_account_and_statuses TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.mppts TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.mppts TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.mppts TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notes TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notes TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notes TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notification_parameters TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notification_parameters TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notification_parameters TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notifications TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notifications TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.notifications TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.orders TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.orders TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.orders TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.organizations TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.organizations TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.organizations TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_site_submissions TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_site_submissions TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_site_submissions TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_sites TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_sites TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.pd_sites TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.routers TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.routers TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.routers TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.solcast_cache TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.solcast_cache TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.solcast_cache TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.transactions TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.transactions TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.transactions TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.ussd_session_hops TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.ussd_session_hops TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.ussd_session_hops TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.ussd_sessions TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.ussd_sessions TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.ussd_sessions TO service_role; + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.wallets TO anon; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.wallets TO authenticated; +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE public.wallets TO service_role; + +GRANT ALL ON SEQUENCE public.accounts_id_seq TO anon; +GRANT ALL ON SEQUENCE public.accounts_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.accounts_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.agents_id_seq TO anon; +GRANT ALL ON SEQUENCE public.agents_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.agents_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.api_keys_id_seq TO anon; +GRANT ALL ON SEQUENCE public.api_keys_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.api_keys_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.audits_id_seq TO anon; +GRANT ALL ON SEQUENCE public.audits_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.audits_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.banks_id_seq TO anon; +GRANT ALL ON SEQUENCE public.banks_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.banks_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.connection_requested_meters_id_seq TO anon; +GRANT ALL ON SEQUENCE public.connection_requested_meters_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.connection_requested_meters_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.connections_id_seq TO anon; +GRANT ALL ON SEQUENCE public.connections_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.connections_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.customers_id_seq TO anon; +GRANT ALL ON SEQUENCE public.customers_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.customers_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.dcus_id_seq TO anon; +GRANT ALL ON SEQUENCE public.dcus_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.dcus_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.meter_command_batch_executions_id_seq TO anon; +GRANT ALL ON SEQUENCE public.meter_command_batch_executions_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.meter_command_batch_executions_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.meter_command_batches_id_seq TO anon; +GRANT ALL ON SEQUENCE public.meter_command_batches_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.meter_command_batches_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.energy_cabins_id_seq TO anon; +GRANT ALL ON SEQUENCE public.energy_cabins_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.energy_cabins_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.grids_id_seq TO anon; +GRANT ALL ON SEQUENCE public.grids_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.grids_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.issues_id_seq TO anon; +GRANT ALL ON SEQUENCE public.issues_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.issues_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.members_id_seq TO anon; +GRANT ALL ON SEQUENCE public.members_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.members_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.meter_commissionings_id_seq TO anon; +GRANT ALL ON SEQUENCE public.meter_commissionings_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.meter_commissionings_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.meter_interactions_id_seq TO anon; +GRANT ALL ON SEQUENCE public.meter_interactions_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.meter_interactions_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.metering_hardware_imports_id_seq TO anon; +GRANT ALL ON SEQUENCE public.metering_hardware_imports_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.metering_hardware_imports_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.metering_hardware_install_sessions_id_seq TO anon; +GRANT ALL ON SEQUENCE public.metering_hardware_install_sessions_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.metering_hardware_install_sessions_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.meters_id_seq TO anon; +GRANT ALL ON SEQUENCE public.meters_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.meters_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.mppts_id_seq TO anon; +GRANT ALL ON SEQUENCE public.mppts_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.mppts_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.notes_id_seq TO anon; +GRANT ALL ON SEQUENCE public.notes_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.notes_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.notification_parameters_id_seq TO anon; +GRANT ALL ON SEQUENCE public.notification_parameters_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.notification_parameters_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.notifications_id_seq TO anon; +GRANT ALL ON SEQUENCE public.notifications_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.notifications_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.orders_id_seq TO anon; +GRANT ALL ON SEQUENCE public.orders_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.orders_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.organizations_id_seq TO anon; +GRANT ALL ON SEQUENCE public.organizations_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.organizations_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.pd_site_submissions_id_seq TO anon; +GRANT ALL ON SEQUENCE public.pd_site_submissions_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.pd_site_submissions_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.pd_sites_id_seq TO anon; +GRANT ALL ON SEQUENCE public.pd_sites_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.pd_sites_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.poles_id_seq TO anon; +GRANT ALL ON SEQUENCE public.poles_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.poles_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.routers_id_seq TO anon; +GRANT ALL ON SEQUENCE public.routers_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.routers_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.solcast_cache_id_seq TO anon; +GRANT ALL ON SEQUENCE public.solcast_cache_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.solcast_cache_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.transactions_id_seq TO anon; +GRANT ALL ON SEQUENCE public.transactions_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.transactions_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.ussd_session_hops_id_seq TO anon; +GRANT ALL ON SEQUENCE public.ussd_session_hops_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.ussd_session_hops_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.ussd_sessions_id_seq TO anon; +GRANT ALL ON SEQUENCE public.ussd_sessions_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.ussd_sessions_id_seq TO service_role; + +GRANT ALL ON SEQUENCE public.wallets_id_seq TO anon; +GRANT ALL ON SEQUENCE public.wallets_id_seq TO authenticated; +GRANT ALL ON SEQUENCE public.wallets_id_seq TO service_role; + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_account_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_account_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_account_id() TO service_role; + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_connection_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_connection_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_connection_id() TO service_role; + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_customer_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_customer_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_customer_id() TO service_role; + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_customer_id_or_agent_id_or_connec() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_customer_id_or_agent_id_or_connec() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_customer_id_or_agent_id_or_connec() TO service_role; + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_dcu_id_or_meter_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_dcu_id_or_meter_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_dcu_id_or_meter_id() TO service_role; + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_meter_command_batch_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_meter_command_batch_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_meter_command_batch_id() TO service_role; + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_grid_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_grid_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_grid_id() TO service_role; + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_meter_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_meter_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_meter_id() TO service_role; + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_metering_hardware_install_session() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_metering_hardware_install_session() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_metering_hardware_install_session() TO service_role; + +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_order_id() TO anon; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_order_id() TO authenticated; +GRANT ALL ON FUNCTION public.append_rls_organization_id_by_order_id() TO service_role; + +GRANT ALL ON FUNCTION public.find_energy_topup_revenue(grid_id integer, start_date timestamp with time zone, end_date timestamp with time zone) TO anon; +GRANT ALL ON FUNCTION public.find_energy_topup_revenue(grid_id integer, start_date timestamp with time zone, end_date timestamp with time zone) TO authenticated; +GRANT ALL ON FUNCTION public.find_energy_topup_revenue(grid_id integer, start_date timestamp with time zone, end_date timestamp with time zone) TO service_role; + +GRANT ALL ON FUNCTION public.find_top_spenders(grid_id integer, limit_count integer, start_date timestamp with time zone, end_date timestamp with time zone) TO anon; +GRANT ALL ON FUNCTION public.find_top_spenders(grid_id integer, limit_count integer, start_date timestamp with time zone, end_date timestamp with time zone) TO authenticated; +GRANT ALL ON FUNCTION public.find_top_spenders(grid_id integer, limit_count integer, start_date timestamp with time zone, end_date timestamp with time zone) TO service_role; + +GRANT ALL ON FUNCTION public.get_grid_status(grid_id integer) TO anon; +GRANT ALL ON FUNCTION public.get_grid_status(grid_id integer) TO authenticated; +GRANT ALL ON FUNCTION public.get_grid_status(grid_id integer) TO service_role; + +GRANT ALL ON FUNCTION public.handle_new_user() TO anon; +GRANT ALL ON FUNCTION public.handle_new_user() TO authenticated; +GRANT ALL ON FUNCTION public.handle_new_user() TO service_role; + +GRANT ALL ON FUNCTION public.handle_update_user() TO anon; +GRANT ALL ON FUNCTION public.handle_update_user() TO authenticated; +GRANT ALL ON FUNCTION public.handle_update_user() TO service_role; + +GRANT ALL ON FUNCTION public.lock_next_order_and_wallets(uuid uuid) TO anon; +GRANT ALL ON FUNCTION public.lock_next_order_and_wallets(uuid uuid) TO authenticated; +GRANT ALL ON FUNCTION public.lock_next_order_and_wallets(uuid uuid) TO service_role; + +GRANT ALL ON FUNCTION public.rls_check_if_lender() TO anon; +GRANT ALL ON FUNCTION public.rls_check_if_lender() TO authenticated; +GRANT ALL ON FUNCTION public.rls_check_if_lender() TO service_role; + +GRANT ALL ON FUNCTION public.rls_check_if_admin_org_member() TO anon; +GRANT ALL ON FUNCTION public.rls_check_if_admin_org_member() TO authenticated; +GRANT ALL ON FUNCTION public.rls_check_if_admin_org_member() TO service_role; + +GRANT ALL ON FUNCTION public.rls_get_member_org_id() TO anon; +GRANT ALL ON FUNCTION public.rls_get_member_org_id() TO authenticated; +GRANT ALL ON FUNCTION public.rls_get_member_org_id() TO service_role; + +GRANT ALL ON FUNCTION public.rls_org_id_from_grid(integer) TO anon; +GRANT ALL ON FUNCTION public.rls_org_id_from_grid(integer) TO authenticated; +GRANT ALL ON FUNCTION public.rls_org_id_from_grid(integer) TO service_role; + +GRANT ALL ON FUNCTION public.rls_org_id_from_customer(integer) TO anon; +GRANT ALL ON FUNCTION public.rls_org_id_from_customer(integer) TO authenticated; +GRANT ALL ON FUNCTION public.rls_org_id_from_customer(integer) TO service_role; + +GRANT ALL ON FUNCTION public.rls_org_id_from_connection(integer) TO anon; +GRANT ALL ON FUNCTION public.rls_org_id_from_connection(integer) TO authenticated; +GRANT ALL ON FUNCTION public.rls_org_id_from_connection(integer) TO service_role; + +GRANT ALL ON FUNCTION public.rls_org_id_from_agent(integer) TO anon; +GRANT ALL ON FUNCTION public.rls_org_id_from_agent(integer) TO authenticated; +GRANT ALL ON FUNCTION public.rls_org_id_from_agent(integer) TO service_role; + +GRANT ALL ON FUNCTION public.rls_org_id_from_meter(integer) TO anon; +GRANT ALL ON FUNCTION public.rls_org_id_from_meter(integer) TO authenticated; +GRANT ALL ON FUNCTION public.rls_org_id_from_meter(integer) TO service_role; + +GRANT ALL ON FUNCTION public.rls_org_id_from_dcu(integer) TO anon; +GRANT ALL ON FUNCTION public.rls_org_id_from_dcu(integer) TO authenticated; +GRANT ALL ON FUNCTION public.rls_org_id_from_dcu(integer) TO service_role; diff --git a/.scripts/fix-supabase-json-type.js b/supabase/scripts/fix-supabase-json-type.js old mode 100755 new mode 100644 similarity index 81% rename from .scripts/fix-supabase-json-type.js rename to supabase/scripts/fix-supabase-json-type.js index 6c9940c..292bd9d --- a/.scripts/fix-supabase-json-type.js +++ b/supabase/scripts/fix-supabase-json-type.js @@ -2,7 +2,7 @@ /** * Post-processing script to fix the Json type in generated Supabase types. - * + * * The default Json type from Supabase is a recursive union that causes * TypeScript errors when accessing properties. This script replaces it * with a more practical Record type. @@ -11,17 +11,17 @@ const fs = require('fs'); const path = require('path'); -const TARGET_FILE = path.join(__dirname, '../libs/core/src/types/supabase-types.ts'); +const TARGET_FILE = path.join( + __dirname, + '../../libs/core/src/types/supabase-types.ts', +); try { let content = fs.readFileSync(TARGET_FILE, 'utf8'); - - // Find and replace the Json type definition - // The default Supabase Json type is a recursive union that spans multiple lines + const jsonTypeRegex = /export type Json =[\s\S]*?\| Json\[\];/; - const newJsonType = 'export type Json = Record'; - + if (content.includes('export type Json =') && content.includes('| Json[]')) { content = content.replace(jsonTypeRegex, newJsonType); fs.writeFileSync(TARGET_FILE, content, 'utf8'); @@ -33,4 +33,3 @@ try { console.error('✗ Error fixing Json type:', error.message); process.exit(1); } - diff --git a/supabase/seed.sql b/supabase/seed.sql new file mode 100644 index 0000000..9e55407 --- /dev/null +++ b/supabase/seed.sql @@ -0,0 +1,218 @@ +-- Local-dev Foundation seed (002d Task 5). +-- Runs after migrations on `pnpm exec supabase db reset` (see supabase/config.toml [db.seed]). +-- Not for remote/prod bootstrap. +-- +-- 1. organizations + wallets +-- 2. auth users → handle_new_user creates bare accounts +-- 3. app_metadata claims → handle_update_user; then members +-- 4. api_keys + grid on solar org + +-- ============================================================================= +-- Organizations +-- ============================================================================= + +INSERT INTO public.organizations (id, name, organization_type) +VALUES + (1, 'NXT Platform Operator', 'PLATFORM_OPERATOR'), + (2, 'NXT Solar Developer', 'SOLAR_DEVELOPER'); + +SELECT setval( + pg_get_serial_sequence('public.organizations', 'id'), + (SELECT MAX(id) FROM public.organizations) +); + +-- ============================================================================= +-- Wallets (1:1 with org for bootstrap; trigger may set rls_organization_id) +-- ============================================================================= + +-- BEFORE INSERT trigger copies organization_id → rls_organization_id. +INSERT INTO public.wallets (id, organization_id, wallet_type, balance) +VALUES + (1, 1, 'REAL', 0), + (2, 2, 'REAL', 0); + +SELECT setval( + pg_get_serial_sequence('public.wallets', 'id'), + (SELECT MAX(id) FROM public.wallets) +); + +-- ============================================================================= +-- Auth users (Step 2) +-- ============================================================================= +-- Fixed UUIDs so later steps can UPDATE by id. +-- AFTER INSERT trigger public.handle_new_user() creates public.accounts +-- (supabase_id, email, full_name, telegram_link_token) — organization_id stays +-- null until app_metadata is set in Step 3. +-- auth.identities rows are required for email/password sign-in. + +INSERT INTO auth.users ( + instance_id, + id, + aud, + role, + email, + encrypted_password, + email_confirmed_at, + raw_app_meta_data, + raw_user_meta_data, + created_at, + updated_at, + confirmation_token, + recovery_token, + email_change_token_new, + email_change +) +VALUES + ( + '00000000-0000-0000-0000-000000000000', + 'a0000000-0000-4000-8000-000000000001', + 'authenticated', + 'authenticated', + 'superadmin@nxt-platform.com', + extensions.crypt('superadmin', extensions.gen_salt('bf')), + now(), + '{"provider": "email", "providers": ["email"]}'::jsonb, + '{"full_name": "Platform Superadmin"}'::jsonb, + now(), + now(), + '', + '', + '', + '' + ), + ( + '00000000-0000-0000-0000-000000000000', + 'a0000000-0000-4000-8000-000000000002', + 'authenticated', + 'authenticated', + 'admin@nxt-solar.com', + extensions.crypt('admin', extensions.gen_salt('bf')), + now(), + '{"provider": "email", "providers": ["email"]}'::jsonb, + '{"full_name": "Solar Admin"}'::jsonb, + now(), + now(), + '', + '', + '', + '' + ); + +INSERT INTO auth.identities ( + provider_id, + user_id, + identity_data, + provider, + last_sign_in_at, + created_at, + updated_at +) +VALUES + ( + 'a0000000-0000-4000-8000-000000000001', + 'a0000000-0000-4000-8000-000000000001', + jsonb_build_object( + 'sub', 'a0000000-0000-4000-8000-000000000001', + 'email', 'superadmin@nxt-platform.com', + 'email_verified', true, + 'phone_verified', false + ), + 'email', + now(), + now(), + now() + ), + ( + 'a0000000-0000-4000-8000-000000000002', + 'a0000000-0000-4000-8000-000000000002', + jsonb_build_object( + 'sub', 'a0000000-0000-4000-8000-000000000002', + 'email', 'admin@nxt-solar.com', + 'email_verified', true, + 'phone_verified', false + ), + 'email', + now(), + now(), + now() + ); + +-- ============================================================================= +-- App metadata + members (Step 3) — mirrors inviteMember flow +-- ============================================================================= +-- 1) UPDATE auth.users.raw_app_meta_data with ACL claims (account_id from the +-- account the insert trigger just created). +-- 2) AFTER UPDATE trigger public.handle_update_user() copies organization_id +-- onto public.accounts. +-- 3) INSERT public.members; BEFORE INSERT trigger sets rls_organization_id +-- from accounts.organization_id (so step 2 must run first). + +UPDATE auth.users AS u +SET + raw_app_meta_data = coalesce(u.raw_app_meta_data, '{}'::jsonb) || jsonb_build_object( + 'account_id', a.id, + 'account_type', 'MEMBER', + 'member_type', 'SUPERADMIN', + 'organization_id', 1 + ), + updated_at = now() +FROM public.accounts AS a +WHERE + u.id = 'a0000000-0000-4000-8000-000000000001' + AND a.supabase_id = u.id; + +UPDATE auth.users AS u +SET + raw_app_meta_data = coalesce(u.raw_app_meta_data, '{}'::jsonb) || jsonb_build_object( + 'account_id', a.id, + 'account_type', 'MEMBER', + 'member_type', 'DEVELOPER', + 'organization_id', 2 + ), + updated_at = now() +FROM public.accounts AS a +WHERE + u.id = 'a0000000-0000-4000-8000-000000000002' + AND a.supabase_id = u.id; + +INSERT INTO public.members (account_id, member_type) +SELECT a.id, 'SUPERADMIN'::public.member_type_enum +FROM public.accounts AS a +WHERE a.supabase_id = 'a0000000-0000-4000-8000-000000000001'; + +INSERT INTO public.members (account_id, member_type) +SELECT a.id, 'DEVELOPER'::public.member_type_enum +FROM public.accounts AS a +WHERE a.supabase_id = 'a0000000-0000-4000-8000-000000000002'; + +SELECT setval( + pg_get_serial_sequence('public.members', 'id'), + (SELECT MAX(id) FROM public.members) +); + +-- ============================================================================= +-- API key + grid (Step 4) +-- ============================================================================= +-- One known key on the platform superadmin account (X-API-KEY / Task 7–8). +-- Grid belongs to NXT Solar Developer (organization_id = 2). + +INSERT INTO public.api_keys (id, key, account_id) +SELECT + 1, + 'dev-api-key-platform-superadmin', + a.id +FROM public.accounts AS a +WHERE a.supabase_id = 'a0000000-0000-4000-8000-000000000001'; + +SELECT setval( + pg_get_serial_sequence('public.api_keys', 'id'), + (SELECT MAX(id) FROM public.api_keys) +); + +INSERT INTO public.grids (id, name, organization_id) +VALUES (1, 'Demo Solar Grid', 2); + +SELECT setval( + pg_get_serial_sequence('public.grids', 'id'), + (SELECT MAX(id) FROM public.grids) +); diff --git a/tsconfig.base.json b/tsconfig.base.json index b71eba6..885e17f 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1,77 +1,23 @@ { - "compileOnSave": false, "compilerOptions": { - "rootDir": ".", - "sourceMap": true, - "declaration": false, - "moduleResolution": "node", + "composite": true, + "declarationMap": true, + "emitDeclarationOnly": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "importHelpers": true, - "target": "es2015", - "module": "esnext", - "lib": [ - "es2024", - "dom", - "dom.iterable" - ], - "types": [ - "multer", - "express", - "node", - "jest" - ], + "isolatedModules": true, + "lib": ["es2022"], + "module": "nodenext", + "moduleResolution": "nodenext", + "noEmitOnError": true, + "noFallthroughCasesInSwitch": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noUnusedLocals": true, "skipLibCheck": true, - "skipDefaultLibCheck": true, - "baseUrl": ".", - "paths": { - "@core": [ - "libs/core/src" - ], - "@core/*": [ - "libs/core/src/*" - ], - "@helpers": [ - "libs/helpers/src" - ], - "@helpers/*": [ - "libs/helpers/src/*" - ], - "@tiamat": [ - "apps/tiamat/src" - ], - "@tiamat/*": [ - "apps/tiamat/src/*" - ], - "@yeti": [ - "apps/yeti/src" - ], - "@yeti/*": [ - "apps/yeti/src/*" - ], - "@loch": [ - "apps/loch/src" - ], - "@loch/*": [ - "apps/loch/src/*" - ], - "@talos": [ - "apps/talos/src" - ], - "@talos/*": [ - "apps/talos/src/*" - ], - "@timeseries": [ - "libs/timeseries/src" - ], - "@timeseries/*": [ - "libs/timeseries/src/*" - ] - } - }, - "exclude": [ - "node_modules", - "tmp", - "supabase" - ] + "strict": true, + "target": "es2022", + "customConditions": ["source"] + } } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..0b3febe --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "./tsconfig.base.json", + "compileOnSave": false, + "files": [], + "references": [ + { + "path": "./apps/api" + }, + { + "path": "./apps/worker" + }, + { + "path": "./libs/core" + } + ] +}