From bcf932d2ff3cd5dabddb0915eae2f97a8747ec75 Mon Sep 17 00:00:00 2001 From: "Sven (AAMOS AI)" Date: Thu, 6 Aug 2026 23:25:32 +0700 Subject: [PATCH] Fas 2 steg 2: Inventory Trust Engine - decay-profiler och cache-jobb --- apps/api/src/lib/helpers.ts | 16 + apps/api/src/routes/inventory.ts | 43 +- apps/api/test/inventory.test.ts | 108 + apps/worker/src/index.ts | 12 + apps/worker/src/processors/maintenance.ts | 54 +- .../migrations/0008_exotic_fallen_one.sql | 3 + .../migrations/0009_lovely_stingray.sql | 19 + .../migrations/meta/0008_snapshot.json | 8865 ++++++++++++++++ .../migrations/meta/0009_snapshot.json | 8992 +++++++++++++++++ infrastructure/migrations/meta/_journal.json | 14 + packages/database/src/schema/_shared.ts | 2 - packages/database/src/schema/decay.ts | 32 + packages/database/src/schema/index.ts | 1 + packages/database/src/schema/inventory.ts | 4 +- packages/database/src/seed/run.ts | 13 + packages/inventory-engine/src/trust.ts | 70 +- packages/inventory-engine/test/trust.test.ts | 109 +- 17 files changed, 18296 insertions(+), 61 deletions(-) create mode 100644 apps/api/test/inventory.test.ts create mode 100644 infrastructure/migrations/0008_exotic_fallen_one.sql create mode 100644 infrastructure/migrations/0009_lovely_stingray.sql create mode 100644 infrastructure/migrations/meta/0008_snapshot.json create mode 100644 infrastructure/migrations/meta/0009_snapshot.json create mode 100644 packages/database/src/schema/decay.ts diff --git a/apps/api/src/lib/helpers.ts b/apps/api/src/lib/helpers.ts index bcec8a2..44fe8e5 100644 --- a/apps/api/src/lib/helpers.ts +++ b/apps/api/src/lib/helpers.ts @@ -2,6 +2,7 @@ import { createHash, randomBytes, randomUUID } from "node:crypto"; import { and, eq } from "drizzle-orm"; import type { Database } from "@app/database"; import { schema } from "@app/database"; +import type { DecayProfile } from "@app/inventory-engine"; import type { EventType } from "@app/shared-types"; import type { NewDomainEvent } from "@app/events"; import { errors } from "./errors.js"; @@ -145,3 +146,18 @@ export async function audit( export function newCorrelationId(): string { return randomUUID(); } + +/** Hämta den aktiva trust-decay-profilen. Cachas i praktiken av databasen; + * funktionen returnerar alltid en giltig profil (default fallback). */ +export async function getActiveDecayProfile(db: Database): Promise { + const [profile] = await db + .select({ halfLifeDays: schema.inventoryDecayProfiles.halfLifeDays, staleAfterDays: schema.inventoryDecayProfiles.staleAfterDays }) + .from(schema.inventoryDecayProfiles) + .where(eq(schema.inventoryDecayProfiles.active, true)) + .orderBy(schema.inventoryDecayProfiles.createdAt) + .limit(1); + return { + halfLifeDays: profile?.halfLifeDays ?? 7, + staleAfterDays: profile?.staleAfterDays ?? 30, + }; +} diff --git a/apps/api/src/routes/inventory.ts b/apps/api/src/routes/inventory.ts index bfa420e..d3aac67 100644 --- a/apps/api/src/routes/inventory.ts +++ b/apps/api/src/routes/inventory.ts @@ -10,7 +10,7 @@ import { } from "@app/validation"; import { classifyExpiry, findDuplicateCandidates, normalizeDelta, computeTrust } from "@app/inventory-engine"; import { errors, parse } from "../lib/errors.js"; -import { emitEvent, requireActiveHousehold, requireMembership } from "../lib/helpers.js"; +import { emitEvent, getActiveDecayProfile, requireActiveHousehold, requireMembership } from "../lib/helpers.js"; /** * Food Twin – lagret (spec §8). Transaktionsbaserat: varje förändring skrivs @@ -22,6 +22,7 @@ export async function inventoryRoutes(app: FastifyInstance) { app.get("/v1/inventory", auth, async (req) => { const query = parse(inventoryQuerySchema, req.query); const householdId = await requireActiveHousehold(app.db, req.userId); + const decayProfile = await getActiveDecayProfile(app.db); const conditions = [ eq(schema.inventoryItems.householdId, householdId), @@ -73,11 +74,24 @@ export async function inventoryRoutes(app: FastifyInstance) { storageLocationType: r.locationType, shelfLifeGuidance: r.shelfLife, }); + const trust = computeTrust( + { + confidence: r.item.confidence, + verifiedByUser: r.item.verifiedByUser, + lastVerifiedAt: r.item.lastVerifiedAt, + quantity: r.item.quantity, + updatedAt: r.item.updatedAt, + }, + new Date(), + decayProfile, + ); return { ...r.item, locationName: r.locationName, locationType: r.locationType, expiry, + trustState: trust.state, + trustScore: trust.score, }; }); @@ -90,6 +104,7 @@ export async function inventoryRoutes(app: FastifyInstance) { /** Varor som bör användas snart – driver "använd först" (spec §4.4, §40). */ app.get("/v1/inventory/expiring", auth, async (req) => { const householdId = await requireActiveHousehold(app.db, req.userId); + const decayProfile = await getActiveDecayProfile(app.db); const rows = await app.db .select({ item: schema.inventoryItems, @@ -114,9 +129,8 @@ export async function inventoryRoutes(app: FastifyInstance) { ); const withExpiry = rows - .map((r) => ({ - ...r.item, - expiry: classifyExpiry({ + .map((r) => { + const expiry = classifyExpiry({ bestBeforeDate: r.item.bestBeforeDate, useByDate: r.item.useByDate, openedAt: r.item.openedAt, @@ -125,8 +139,25 @@ export async function inventoryRoutes(app: FastifyInstance) { purchasedAt: r.item.purchasedAt, storageLocationType: r.locationType, shelfLifeGuidance: r.shelfLife, - }), - })) + }); + const trust = computeTrust( + { + confidence: r.item.confidence, + verifiedByUser: r.item.verifiedByUser, + lastVerifiedAt: r.item.lastVerifiedAt, + quantity: r.item.quantity, + updatedAt: r.item.updatedAt, + }, + new Date(), + decayProfile, + ); + return { + ...r.item, + expiry, + trustState: trust.state, + trustScore: trust.score, + }; + }) .filter( (i) => i.expiry.status === "expiring" || diff --git a/apps/api/test/inventory.test.ts b/apps/api/test/inventory.test.ts new file mode 100644 index 0000000..96a8d40 --- /dev/null +++ b/apps/api/test/inventory.test.ts @@ -0,0 +1,108 @@ +import "./setup-env.js"; +import { describe, expect, it, beforeAll, afterAll } from "vitest"; +import { eq, inArray } from "drizzle-orm"; +import { buildServer } from "../src/server.js"; +import { loadConfig } from "../src/config.js"; +import { createDatabase, closeDatabase, schema } from "@app/database"; + +describe("inventory trust read-time computation", () => { + const testDb = createDatabase(process.env.TEST_DATABASE_URL!); + const config = loadConfig(); + let app: Awaited>; + let accessToken: string; + let householdId: string; + let locationId: string; + const userEmail = "trust-read@example.invalid"; + + async function cleanup() { + const existing = await testDb.db + .select({ id: schema.users.id }) + .from(schema.users) + .where(inArray(schema.users.email, [userEmail])); + for (const u of existing) { + await testDb.db.delete(schema.inventoryTransactions).where(eq(schema.inventoryTransactions.actorUserId, u.id)); + const owned = await testDb.db + .select({ id: schema.households.id }) + .from(schema.households) + .innerJoin(schema.householdMembers, eq(schema.householdMembers.householdId, schema.households.id)) + .where(eq(schema.householdMembers.userId, u.id)); + for (const h of owned) { + await testDb.db.delete(schema.inventoryItems).where(eq(schema.inventoryItems.householdId, h.id)); + await testDb.db.delete(schema.storageLocations).where(eq(schema.storageLocations.householdId, h.id)); + await testDb.db.delete(schema.households).where(eq(schema.households.id, h.id)); + } + await testDb.db.delete(schema.householdMembers).where(eq(schema.householdMembers.userId, u.id)); + await testDb.db.delete(schema.userPreferences).where(eq(schema.userPreferences.userId, u.id)); + await testDb.db.delete(schema.users).where(eq(schema.users.id, u.id)); + } + } + + beforeAll(async () => { + await cleanup(); + app = await buildServer(config); + await app.ready(); + + const res = await app.inject({ + method: "POST", + url: "/v1/auth/register", + payload: { email: userEmail, password: "Password123!", displayName: "Trust" }, + }); + const body = JSON.parse(res.body) as { accessToken: string }; + accessToken = body.accessToken; + + const quick = await app.inject({ + method: "POST", + url: "/v1/onboarding/quick-start", + headers: { authorization: `Bearer ${accessToken}` }, + payload: { goals: ["cook_more"], precisionMode: "simple" }, + }); + const quickBody = JSON.parse(quick.body) as { householdId: string }; + householdId = quickBody.householdId; + + const [location] = await testDb.db + .select() + .from(schema.storageLocations) + .where(eq(schema.storageLocations.householdId, householdId)) + .limit(1); + locationId = location!.id; + }); + + afterAll(async () => { + await cleanup(); + await closeDatabase(); + await app.close(); + }); + + it("reports decaying/stale for items with old lastVerifiedAt without a write", async () => { + const fortyDaysAgo = new Date(Date.now() - 40 * 86_400_000); + + const [item] = await testDb.db + .insert(schema.inventoryItems) + .values({ + householdId, + displayName: "Gammal mjölk", + quantity: 1, + unit: "LITER", + storageLocationId: locationId, + source: "manual_search", + confidence: 0.6, + verifiedByUser: true, + lastVerifiedAt: fortyDaysAgo, + updatedAt: fortyDaysAgo, + }) + .returning(); + + const res = await app.inject({ + method: "GET", + url: "/v1/inventory", + headers: { authorization: `Bearer ${accessToken}` }, + }); + expect(res.statusCode).toBe(200); + + const body = JSON.parse(res.body) as { items: Array<{ id: string; trustState: string; trustScore: number }> }; + const found = body.items.find((i) => i.id === item!.id); + expect(found).toBeTruthy(); + expect(["decaying", "stale"]).toContain(found!.trustState); + expect(found!.trustScore).toBeLessThan(60); + }); +}); diff --git a/apps/worker/src/index.ts b/apps/worker/src/index.ts index f723cb3..1327707 100644 --- a/apps/worker/src/index.ts +++ b/apps/worker/src/index.ts @@ -16,6 +16,7 @@ import { processStoreNotification, processSubscriptionSweep, processTrainingExport, + processTrustDecay, } from "./processors/maintenance.js"; import { @@ -110,6 +111,12 @@ const worker = new Worker( return; } + case "UPDATE_TRUST_STATES": { + const updated = await processTrustDecay(ctx); + if (updated > 0) log(`Trust decay: ${updated} items uppdaterade`); + return; + } + // Deterministiska/planerade jobb som inte kräver egen processor ännu case "NORMALIZE_PRODUCTS": case "DEDUPLICATE_INVENTORY": @@ -201,6 +208,11 @@ async function registerRepeatableJobs() { { every: 30_000 }, { name: "PUBLISH_OUTBOX", data: { jobType: "PUBLISH_OUTBOX" }, opts: baseOpts }, ); + await queue.upsertJobScheduler( + "scheduler-trust", + { every: 300_000 }, + { name: "UPDATE_TRUST_STATES", data: { jobType: "UPDATE_TRUST_STATES" }, opts: baseOpts }, + ); await queue.upsertJobScheduler( "scheduler-expiry", { pattern: "0 7 * * *", tz: "Europe/Stockholm" }, diff --git a/apps/worker/src/processors/maintenance.ts b/apps/worker/src/processors/maintenance.ts index 0df3dcd..9dfb4b1 100644 --- a/apps/worker/src/processors/maintenance.ts +++ b/apps/worker/src/processors/maintenance.ts @@ -1,6 +1,6 @@ import { and, eq, gt, inArray, isNull, lte, sql } from "drizzle-orm"; import { schema } from "@app/database"; -import { classifyExpiry } from "@app/inventory-engine"; +import { classifyExpiry, computeTrust } from "@app/inventory-engine"; import { deriveMemoryUpdates } from "@app/memory-client"; import { getLocaleContext } from "../locale.js"; import type { WorkerContext } from "../context.js"; @@ -31,6 +31,58 @@ export async function processOutbox(ctx: WorkerContext): Promise { return pending.length; } +/** + * UPDATE_TRUST_STATES (Fas 2 §5.2): uppdatera cache-kolumnen inventory_items.trust_state + * utifrån aktiv decay-profil. Körs periodiskt. Decay påverkar endast förtroende, + * aldrig ätbarhet (mjölkprincipen D-035). + */ +export async function processTrustDecay(ctx: WorkerContext): Promise { + const [profile] = await ctx.db + .select({ + halfLifeDays: schema.inventoryDecayProfiles.halfLifeDays, + staleAfterDays: schema.inventoryDecayProfiles.staleAfterDays, + }) + .from(schema.inventoryDecayProfiles) + .where(eq(schema.inventoryDecayProfiles.active, true)) + .orderBy(schema.inventoryDecayProfiles.createdAt) + .limit(1); + + const decayProfile = { + halfLifeDays: profile?.halfLifeDays ?? 7, + staleAfterDays: profile?.staleAfterDays ?? 30, + }; + + const now = new Date(); + const rows = await ctx.db + .select() + .from(schema.inventoryItems) + .where(and(isNull(schema.inventoryItems.depletedAt), gt(schema.inventoryItems.quantity, 0))) + .limit(500); + + let updated = 0; + for (const item of rows) { + const { state } = computeTrust( + { + confidence: item.confidence, + verifiedByUser: item.verifiedByUser, + lastVerifiedAt: item.lastVerifiedAt, + quantity: item.quantity, + updatedAt: item.updatedAt, + }, + now, + decayProfile, + ); + if (state !== item.trustState) { + await ctx.db + .update(schema.inventoryItems) + .set({ trustState: state, updatedAt: now }) + .where(eq(schema.inventoryItems.id, item.id)); + updated++; + } + } + return updated; +} + /** SEND_EXPIRY_NOTIFICATION (spec §54): skapa notiser för varor som snart går ut. */ export async function processExpiryNotifications(ctx: WorkerContext): Promise { const households = await ctx.db.select({ id: schema.households.id }).from(schema.households); diff --git a/infrastructure/migrations/0008_exotic_fallen_one.sql b/infrastructure/migrations/0008_exotic_fallen_one.sql new file mode 100644 index 0000000..2a46777 --- /dev/null +++ b/infrastructure/migrations/0008_exotic_fallen_one.sql @@ -0,0 +1,3 @@ +ALTER TABLE "inventory_items" ALTER COLUMN "trust_state" SET DATA TYPE varchar(16);--> statement-breakpoint +ALTER TABLE "inventory_items" ALTER COLUMN "trust_state" SET DEFAULT 'unverified';--> statement-breakpoint +DROP TYPE "public"."trust_state"; \ No newline at end of file diff --git a/infrastructure/migrations/0009_lovely_stingray.sql b/infrastructure/migrations/0009_lovely_stingray.sql new file mode 100644 index 0000000..45a21b8 --- /dev/null +++ b/infrastructure/migrations/0009_lovely_stingray.sql @@ -0,0 +1,19 @@ +CREATE TABLE "inventory_decay_profiles" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "name" varchar(64) NOT NULL, + "description" text, + "half_life_days" double precision DEFAULT 7 NOT NULL, + "stale_after_days" integer DEFAULT 30 NOT NULL, + "active" boolean DEFAULT true NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "inventory_decay_profiles_name_unique" UNIQUE("name") +); +--> statement-breakpoint +CREATE TABLE "inventory_item_decay_profile" ( + "inventory_item_id" uuid PRIMARY KEY NOT NULL, + "decay_profile_id" uuid NOT NULL +); +--> statement-breakpoint +ALTER TABLE "inventory_item_decay_profile" ADD CONSTRAINT "inventory_item_decay_profile_inventory_item_id_inventory_items_id_fk" FOREIGN KEY ("inventory_item_id") REFERENCES "public"."inventory_items"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "inventory_item_decay_profile" ADD CONSTRAINT "inventory_item_decay_profile_decay_profile_id_inventory_decay_profiles_id_fk" FOREIGN KEY ("decay_profile_id") REFERENCES "public"."inventory_decay_profiles"("id") ON DELETE restrict ON UPDATE no action; \ No newline at end of file diff --git a/infrastructure/migrations/meta/0008_snapshot.json b/infrastructure/migrations/meta/0008_snapshot.json new file mode 100644 index 0000000..afa6cc9 --- /dev/null +++ b/infrastructure/migrations/meta/0008_snapshot.json @@ -0,0 +1,8865 @@ +{ + "id": "b26aaf39-2905-4816-9575-ac7e429c3a28", + "prevId": "a3870bb9-6526-4977-bf4d-312899f6a7b9", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.admin_totp": { + "name": "admin_totp", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "secret_base32": { + "name": "secret_base32", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled_at": { + "name": "enabled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_used_step": { + "name": "last_used_step", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "admin_totp_user_id_users_id_fk": { + "name": "admin_totp_user_id_users_id_fk", + "tableFrom": "admin_totp", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email_verification_tokens": { + "name": "email_verification_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "used_at": { + "name": "used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "email_verification_tokens_user_idx": { + "name": "email_verification_tokens_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "email_verification_tokens_hash_unique": { + "name": "email_verification_tokens_hash_unique", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "email_verification_tokens_user_id_users_id_fk": { + "name": "email_verification_tokens_user_id_users_id_fk", + "tableFrom": "email_verification_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.password_reset_tokens": { + "name": "password_reset_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "used_at": { + "name": "used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "password_reset_tokens_user_idx": { + "name": "password_reset_tokens_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "password_reset_tokens_hash_unique": { + "name": "password_reset_tokens_hash_unique", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "password_reset_tokens_user_id_users_id_fk": { + "name": "password_reset_tokens_user_id_users_id_fk", + "tableFrom": "password_reset_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.refresh_tokens": { + "name": "refresh_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "family_id": { + "name": "family_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "replaced_by_token_id": { + "name": "replaced_by_token_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ip": { + "name": "ip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "refresh_tokens_user_idx": { + "name": "refresh_tokens_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "refresh_tokens_hash_unique": { + "name": "refresh_tokens_hash_unique", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "refresh_tokens_user_id_users_id_fk": { + "name": "refresh_tokens_user_id_users_id_fk", + "tableFrom": "refresh_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_consents": { + "name": "user_consents", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "consent_kind", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "consent_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "granted_at": { + "name": "granted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_consents_user_id_users_id_fk": { + "name": "user_consents_user_id_users_id_fk", + "tableFrom": "user_consents", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_consents_user_id_kind_pk": { + "name": "user_consents_user_id_kind_pk", + "columns": [ + "user_id", + "kind" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_credentials": { + "name": "user_credentials", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "password_hash": { + "name": "password_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password_updated_at": { + "name": "password_updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_credentials_user_id_users_id_fk": { + "name": "user_credentials_user_id_users_id_fk", + "tableFrom": "user_credentials", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_health_profiles": { + "name": "user_health_profiles", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "birth_year": { + "name": "birth_year", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "sex": { + "name": "sex", + "type": "sex", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "height_cm": { + "name": "height_cm", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "weight_kg": { + "name": "weight_kg", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "target_weight_kg": { + "name": "target_weight_kg", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "activity_level": { + "name": "activity_level", + "type": "activity_level", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'moderate'" + }, + "training_sessions_per_week": { + "name": "training_sessions_per_week", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "training_types": { + "name": "training_types", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_health_profiles_user_id_users_id_fk": { + "name": "user_health_profiles_user_id_users_id_fk", + "tableFrom": "user_health_profiles", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_preferences": { + "name": "user_preferences", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "primary_goal": { + "name": "primary_goal", + "type": "goal_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "goals": { + "name": "goals", + "type": "goal_type[]", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "diet_pattern": { + "name": "diet_pattern", + "type": "diet_pattern", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'omnivore'" + }, + "religious_rule": { + "name": "religious_rule", + "type": "religious_rule", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "allergens": { + "name": "allergens", + "type": "allergen[]", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "intolerances": { + "name": "intolerances", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "avoid_ingredient_ids": { + "name": "avoid_ingredient_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "favorite_cuisines": { + "name": "favorite_cuisines", + "type": "cuisine[]", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "disliked_dishes": { + "name": "disliked_dishes", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "spice_level_max": { + "name": "spice_level_max", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 3 + }, + "weekly_budget_minor": { + "name": "weekly_budget_minor", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "max_cooking_minutes_weekday": { + "name": "max_cooking_minutes_weekday", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "equipment": { + "name": "equipment", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "default_portions": { + "name": "default_portions", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 2 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_preferences_user_id_users_id_fk": { + "name": "user_preferences_user_id_users_id_fk", + "tableFrom": "user_preferences", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified_at": { + "name": "email_verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "user_role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "locale": { + "name": "locale", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'sv-SE'" + }, + "precision_mode": { + "name": "precision_mode", + "type": "precision_mode", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'simple'" + }, + "onboarding_completed": { + "name": "onboarding_completed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "onboarding_step": { + "name": "onboarding_step", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'a'" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "users_email_unique": { + "name": "users_email_unique", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_locale_preferences": { + "name": "user_locale_preferences", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "language_tag": { + "name": "language_tag", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'sv-SE'" + }, + "region_code": { + "name": "region_code", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'SE'" + }, + "time_zone": { + "name": "time_zone", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'Europe/Stockholm'" + }, + "measurement_system": { + "name": "measurement_system", + "type": "measurement_system", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'METRIC'" + }, + "temperature_unit": { + "name": "temperature_unit", + "type": "temperature_unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'CELSIUS'" + }, + "currency_code": { + "name": "currency_code", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'SEK'" + }, + "first_day_of_week": { + "name": "first_day_of_week", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "use_24_hour_time": { + "name": "use_24_hour_time", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_locale_preferences_user_id_users_id_fk": { + "name": "user_locale_preferences_user_id_users_id_fk", + "tableFrom": "user_locale_preferences", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.household_members": { + "name": "household_members", + "schema": "", + "columns": { + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "household_role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "portion_factor": { + "name": "portion_factor", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "joined_at": { + "name": "joined_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "household_members_user_idx": { + "name": "household_members_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "household_members_household_id_households_id_fk": { + "name": "household_members_household_id_households_id_fk", + "tableFrom": "household_members", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "household_members_user_id_users_id_fk": { + "name": "household_members_user_id_users_id_fk", + "tableFrom": "household_members", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "household_members_household_id_user_id_pk": { + "name": "household_members_household_id_user_id_pk", + "columns": [ + "household_id", + "user_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.households": { + "name": "households", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invite_code": { + "name": "invite_code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "weekly_budget_minor": { + "name": "weekly_budget_minor", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "first_scan_completed_at": { + "name": "first_scan_completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "fifth_item_confirmed_at": { + "name": "fifth_item_confirmed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "first_recipe_recommendation_viewed_at": { + "name": "first_recipe_recommendation_viewed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "first_recipe_saved_or_started_at": { + "name": "first_recipe_saved_or_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "activated_at": { + "name": "activated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "first_cooking_session_completed_at": { + "name": "first_cooking_session_completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "inventory_updated_after_cooking_at": { + "name": "inventory_updated_after_cooking_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "value_confirmed_at": { + "name": "value_confirmed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "currency_code": { + "name": "currency_code", + "type": "varchar(3)", + "primaryKey": false, + "notNull": true, + "default": "'SEK'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "households_invite_code_unique": { + "name": "households_invite_code_unique", + "columns": [ + { + "expression": "invite_code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.storage_locations": { + "name": "storage_locations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "storage_location_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sublocations": { + "name": "sublocations", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "storage_locations_household_idx": { + "name": "storage_locations_household_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "storage_locations_household_id_households_id_fk": { + "name": "storage_locations_household_id_households_id_fk", + "tableFrom": "storage_locations", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.canonical_ingredients": { + "name": "canonical_ingredients", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name_sv": { + "name": "name_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name_en": { + "name": "name_en", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "aliases": { + "name": "aliases", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "default_unit": { + "name": "default_unit", + "type": "unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "density_g_per_ml": { + "name": "density_g_per_ml", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "grams_per_piece": { + "name": "grams_per_piece", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "allergens": { + "name": "allergens", + "type": "allergen[]", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "is_vegan": { + "name": "is_vegan", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_vegetarian": { + "name": "is_vegetarian", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "contains_gluten": { + "name": "contains_gluten", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "contains_lactose": { + "name": "contains_lactose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_pork": { + "name": "is_pork", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_beef": { + "name": "is_beef", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_alcohol": { + "name": "is_alcohol", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "nutrition_per_100": { + "name": "nutrition_per_100", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "nutrition_provenance": { + "name": "nutrition_provenance", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "peak_seasons": { + "name": "peak_seasons", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "shelf_life_guidance": { + "name": "shelf_life_guidance", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "default_price_minor_per_kg": { + "name": "default_price_minor_per_kg", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "canonical_ingredients_category_idx": { + "name": "canonical_ingredients_category_idx", + "columns": [ + { + "expression": "category", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.substitutions": { + "name": "substitutions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "from_ingredient_id": { + "name": "from_ingredient_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "to_ingredient_id": { + "name": "to_ingredient_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ratio": { + "name": "ratio", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "instructions_sv": { + "name": "instructions_sv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "best_for": { + "name": "best_for", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "not_recommended_for": { + "name": "not_recommended_for", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "flavor_impact_sv": { + "name": "flavor_impact_sv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "texture_impact_sv": { + "name": "texture_impact_sv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "substitutions_from_idx": { + "name": "substitutions_from_idx", + "columns": [ + { + "expression": "from_ingredient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "substitutions_pair_unique": { + "name": "substitutions_pair_unique", + "columns": [ + { + "expression": "from_ingredient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "to_ingredient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "substitutions_from_ingredient_id_canonical_ingredients_id_fk": { + "name": "substitutions_from_ingredient_id_canonical_ingredients_id_fk", + "tableFrom": "substitutions", + "tableTo": "canonical_ingredients", + "columnsFrom": [ + "from_ingredient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "substitutions_to_ingredient_id_canonical_ingredients_id_fk": { + "name": "substitutions_to_ingredient_id_canonical_ingredients_id_fk", + "tableFrom": "substitutions", + "tableTo": "canonical_ingredients", + "columnsFrom": [ + "to_ingredient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.products": { + "name": "products", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "gtin": { + "name": "gtin", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "brand": { + "name": "brand", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "canonical_ingredient_id": { + "name": "canonical_ingredient_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "package_size_value": { + "name": "package_size_value", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "package_size_unit": { + "name": "package_size_unit", + "type": "unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "ingredients_text": { + "name": "ingredients_text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "allergens": { + "name": "allergens", + "type": "allergen[]", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "may_contain_allergens": { + "name": "may_contain_allergens", + "type": "allergen[]", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "nutrition": { + "name": "nutrition", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "image_urls": { + "name": "image_urls", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'sv'" + }, + "market": { + "name": "market", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'SE'" + }, + "data_source": { + "name": "data_source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "verification_status": { + "name": "verification_status", + "type": "verification_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'unverified'" + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "valid_from": { + "name": "valid_from", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "valid_to": { + "name": "valid_to", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "products_gtin_version_unique": { + "name": "products_gtin_version_unique", + "columns": [ + { + "expression": "gtin", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"products\".\"gtin\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "products_gtin_current_idx": { + "name": "products_gtin_current_idx", + "columns": [ + { + "expression": "gtin", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"products\".\"valid_to\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "products_canonical_idx": { + "name": "products_canonical_idx", + "columns": [ + { + "expression": "canonical_ingredient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "products_canonical_ingredient_id_canonical_ingredients_id_fk": { + "name": "products_canonical_ingredient_id_canonical_ingredients_id_fk", + "tableFrom": "products", + "tableTo": "canonical_ingredients", + "columnsFrom": [ + "canonical_ingredient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.inventory_items": { + "name": "inventory_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "canonical_ingredient_id": { + "name": "canonical_ingredient_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "brand": { + "name": "brand", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "quantity": { + "name": "quantity", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "unit": { + "name": "unit", + "type": "unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "storage_location_id": { + "name": "storage_location_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sublocation": { + "name": "sublocation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "purchased_at": { + "name": "purchased_at", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "opened_at": { + "name": "opened_at", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "best_before_date": { + "name": "best_before_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "use_by_date": { + "name": "use_by_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "date_kind": { + "name": "date_kind", + "type": "date_kind", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "frozen_at": { + "name": "frozen_at", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "thawed_at": { + "name": "thawed_at", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "price_minor": { + "name": "price_minor", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "nutrition_per_100": { + "name": "nutrition_per_100", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "inventory_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "confidence": { + "name": "confidence", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "verified_by_user": { + "name": "verified_by_user", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "last_verified_at": { + "name": "last_verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "expiry_status": { + "name": "expiry_status", + "type": "expiry_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "trust_state": { + "name": "trust_state", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true, + "default": "'unverified'" + }, + "depleted_at": { + "name": "depleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "model_version": { + "name": "model_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prompt_version": { + "name": "prompt_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "inventory_items_household_idx": { + "name": "inventory_items_household_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "inventory_items_household_bb_idx": { + "name": "inventory_items_household_bb_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "best_before_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "inventory_items_location_idx": { + "name": "inventory_items_location_idx", + "columns": [ + { + "expression": "storage_location_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "inventory_items_canonical_idx": { + "name": "inventory_items_canonical_idx", + "columns": [ + { + "expression": "canonical_ingredient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "inventory_items_household_id_households_id_fk": { + "name": "inventory_items_household_id_households_id_fk", + "tableFrom": "inventory_items", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "inventory_items_canonical_ingredient_id_canonical_ingredients_id_fk": { + "name": "inventory_items_canonical_ingredient_id_canonical_ingredients_id_fk", + "tableFrom": "inventory_items", + "tableTo": "canonical_ingredients", + "columnsFrom": [ + "canonical_ingredient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inventory_items_product_id_products_id_fk": { + "name": "inventory_items_product_id_products_id_fk", + "tableFrom": "inventory_items", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inventory_items_storage_location_id_storage_locations_id_fk": { + "name": "inventory_items_storage_location_id_storage_locations_id_fk", + "tableFrom": "inventory_items", + "tableTo": "storage_locations", + "columnsFrom": [ + "storage_location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.inventory_transactions": { + "name": "inventory_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "inventory_item_id": { + "name": "inventory_item_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "inventory_transaction_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "quantity_delta": { + "name": "quantity_delta", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "unit": { + "name": "unit", + "type": "unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "ref_type": { + "name": "ref_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ref_id": { + "name": "ref_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "value_minor": { + "name": "value_minor", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "inventory_tx_item_idx": { + "name": "inventory_tx_item_idx", + "columns": [ + { + "expression": "inventory_item_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "inventory_tx_household_time_idx": { + "name": "inventory_tx_household_time_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "inventory_tx_type_idx": { + "name": "inventory_tx_type_idx", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "inventory_transactions_household_id_households_id_fk": { + "name": "inventory_transactions_household_id_households_id_fk", + "tableFrom": "inventory_transactions", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "inventory_transactions_inventory_item_id_inventory_items_id_fk": { + "name": "inventory_transactions_inventory_item_id_inventory_items_id_fk", + "tableFrom": "inventory_transactions", + "tableTo": "inventory_items", + "columnsFrom": [ + "inventory_item_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "inventory_transactions_actor_user_id_users_id_fk": { + "name": "inventory_transactions_actor_user_id_users_id_fk", + "tableFrom": "inventory_transactions", + "tableTo": "users", + "columnsFrom": [ + "actor_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipe_cooks": { + "name": "recipe_cooks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "recipe_id": { + "name": "recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "portions_cooked": { + "name": "portions_cooked", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "cooked_at": { + "name": "cooked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "recipe_cooks_recipe_idx": { + "name": "recipe_cooks_recipe_idx", + "columns": [ + { + "expression": "recipe_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "recipe_cooks_user_idx": { + "name": "recipe_cooks_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "recipe_cooks_recipe_id_recipes_id_fk": { + "name": "recipe_cooks_recipe_id_recipes_id_fk", + "tableFrom": "recipe_cooks", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "recipe_cooks_user_id_users_id_fk": { + "name": "recipe_cooks_user_id_users_id_fk", + "tableFrom": "recipe_cooks", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipe_favorites": { + "name": "recipe_favorites", + "schema": "", + "columns": { + "recipe_id": { + "name": "recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "recipe_favorites_user_idx": { + "name": "recipe_favorites_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "recipe_favorites_recipe_id_recipes_id_fk": { + "name": "recipe_favorites_recipe_id_recipes_id_fk", + "tableFrom": "recipe_favorites", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "recipe_favorites_user_id_users_id_fk": { + "name": "recipe_favorites_user_id_users_id_fk", + "tableFrom": "recipe_favorites", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "recipe_favorites_recipe_id_user_id_pk": { + "name": "recipe_favorites_recipe_id_user_id_pk", + "columns": [ + "recipe_id", + "user_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipe_ingredients": { + "name": "recipe_ingredients", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "recipe_id": { + "name": "recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "canonical_ingredient_id": { + "name": "canonical_ingredient_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name_sv": { + "name": "display_name_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "unit": { + "name": "unit", + "type": "unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "optional": { + "name": "optional", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "recipe_ingredients_recipe_idx": { + "name": "recipe_ingredients_recipe_idx", + "columns": [ + { + "expression": "recipe_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "recipe_ingredients_canonical_idx": { + "name": "recipe_ingredients_canonical_idx", + "columns": [ + { + "expression": "canonical_ingredient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "recipe_ingredients_recipe_id_recipes_id_fk": { + "name": "recipe_ingredients_recipe_id_recipes_id_fk", + "tableFrom": "recipe_ingredients", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "recipe_ingredients_canonical_ingredient_id_canonical_ingredients_id_fk": { + "name": "recipe_ingredients_canonical_ingredient_id_canonical_ingredients_id_fk", + "tableFrom": "recipe_ingredients", + "tableTo": "canonical_ingredients", + "columnsFrom": [ + "canonical_ingredient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipe_ratings": { + "name": "recipe_ratings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "recipe_id": { + "name": "recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "stars": { + "name": "stars", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "feedback_tags": { + "name": "feedback_tags", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "comment": { + "name": "comment", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "recipe_ratings_user_recipe_unique": { + "name": "recipe_ratings_user_recipe_unique", + "columns": [ + { + "expression": "recipe_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "recipe_ratings_recipe_idx": { + "name": "recipe_ratings_recipe_idx", + "columns": [ + { + "expression": "recipe_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "recipe_ratings_recipe_id_recipes_id_fk": { + "name": "recipe_ratings_recipe_id_recipes_id_fk", + "tableFrom": "recipe_ratings", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "recipe_ratings_user_id_users_id_fk": { + "name": "recipe_ratings_user_id_users_id_fk", + "tableFrom": "recipe_ratings", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipe_similarities": { + "name": "recipe_similarities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "recipe_a_id": { + "name": "recipe_a_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recipe_b_id": { + "name": "recipe_b_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "similarity_score": { + "name": "similarity_score", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "classification": { + "name": "classification", + "type": "recipe_similarity_class", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "details": { + "name": "details", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "recipe_similarities_pair_unique": { + "name": "recipe_similarities_pair_unique", + "columns": [ + { + "expression": "recipe_a_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "recipe_b_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "recipe_similarities_recipe_a_id_recipes_id_fk": { + "name": "recipe_similarities_recipe_a_id_recipes_id_fk", + "tableFrom": "recipe_similarities", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_a_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "recipe_similarities_recipe_b_id_recipes_id_fk": { + "name": "recipe_similarities_recipe_b_id_recipes_id_fk", + "tableFrom": "recipe_similarities", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_b_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipe_source_registry": { + "name": "recipe_source_registry", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "source_name": { + "name": "source_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "license": { + "name": "license", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "right_to_store": { + "name": "right_to_store", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "right_to_modify": { + "name": "right_to_modify", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "right_to_display": { + "name": "right_to_display", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "attribution_required": { + "name": "attribution_required", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "attribution_text": { + "name": "attribution_text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "commercial_use": { + "name": "commercial_use", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "valid_from": { + "name": "valid_from", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "valid_to": { + "name": "valid_to", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipe_steps": { + "name": "recipe_steps", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "recipe_id": { + "name": "recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "step_number": { + "name": "step_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "instruction_sv": { + "name": "instruction_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timer_seconds": { + "name": "timer_seconds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "temperature_c": { + "name": "temperature_c", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "tip": { + "name": "tip", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "recipe_steps_recipe_idx": { + "name": "recipe_steps_recipe_idx", + "columns": [ + { + "expression": "recipe_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "recipe_steps_recipe_step_unique": { + "name": "recipe_steps_recipe_step_unique", + "columns": [ + { + "expression": "recipe_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "step_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "recipe_steps_recipe_id_recipes_id_fk": { + "name": "recipe_steps_recipe_id_recipes_id_fk", + "tableFrom": "recipe_steps", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipes": { + "name": "recipes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title_sv": { + "name": "title_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description_sv": { + "name": "description_sv", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "country": { + "name": "country", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cuisine": { + "name": "cuisine", + "type": "cuisine", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "meal_types": { + "name": "meal_types", + "type": "meal_type[]", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "tags": { + "name": "tags", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "methods": { + "name": "methods", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "equipment": { + "name": "equipment", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "difficulty": { + "name": "difficulty", + "type": "recipe_difficulty", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'easy'" + }, + "prep_time_minutes": { + "name": "prep_time_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "cook_time_minutes": { + "name": "cook_time_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_time_minutes": { + "name": "total_time_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "portions": { + "name": "portions", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 4 + }, + "nutrition_per_portion": { + "name": "nutrition_per_portion", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "allergens": { + "name": "allergens", + "type": "allergen[]", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "spice_level": { + "name": "spice_level", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "estimated_cost_minor_per_portion": { + "name": "estimated_cost_minor_per_portion", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "storage_guidance_sv": { + "name": "storage_guidance_sv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "meal_prep_friendly": { + "name": "meal_prep_friendly", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "freezer_friendly": { + "name": "freezer_friendly", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "peak_seasons": { + "name": "peak_seasons", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "holiday_tags": { + "name": "holiday_tags", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "dna": { + "name": "dna", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "variant_type": { + "name": "variant_type", + "type": "recipe_variant_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'standard'" + }, + "variant_of_recipe_id": { + "name": "variant_of_recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "forked_from_recipe_id": { + "name": "forked_from_recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "recipe_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "verification_status": { + "name": "verification_status", + "type": "recipe_verification_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'unverified'" + }, + "source_type": { + "name": "source_type", + "type": "recipe_source_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "source_registry_id": { + "name": "source_registry_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "creator_user_id": { + "name": "creator_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "creator_display_name": { + "name": "creator_display_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "image_urls": { + "name": "image_urls", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "rating_average": { + "name": "rating_average", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "rating_count": { + "name": "rating_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "cook_count": { + "name": "cook_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "favorite_count": { + "name": "favorite_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "moderation_note": { + "name": "moderation_note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "recipes_slug_unique": { + "name": "recipes_slug_unique", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "recipes_status_idx": { + "name": "recipes_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "recipes_cuisine_idx": { + "name": "recipes_cuisine_idx", + "columns": [ + { + "expression": "cuisine", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "recipes_variant_of_idx": { + "name": "recipes_variant_of_idx", + "columns": [ + { + "expression": "variant_of_recipe_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "recipes_creator_idx": { + "name": "recipes_creator_idx", + "columns": [ + { + "expression": "creator_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "recipes_total_time_idx": { + "name": "recipes_total_time_idx", + "columns": [ + { + "expression": "total_time_minutes", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "recipes_source_registry_id_recipe_source_registry_id_fk": { + "name": "recipes_source_registry_id_recipe_source_registry_id_fk", + "tableFrom": "recipes", + "tableTo": "recipe_source_registry", + "columnsFrom": [ + "source_registry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "recipes_creator_user_id_users_id_fk": { + "name": "recipes_creator_user_id_users_id_fk", + "tableFrom": "recipes", + "tableTo": "users", + "columnsFrom": [ + "creator_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ingredient_translations": { + "name": "ingredient_translations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ingredient_id": { + "name": "ingredient_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "language_tag": { + "name": "language_tag", + "type": "varchar(35)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "aliases": { + "name": "aliases", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "source": { + "name": "source", + "type": "translation_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'seed'" + }, + "status": { + "name": "status", + "type": "translation_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'published'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ingredient_translations_unique": { + "name": "ingredient_translations_unique", + "columns": [ + { + "expression": "ingredient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "language_tag", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ingredient_translations_lang_idx": { + "name": "ingredient_translations_lang_idx", + "columns": [ + { + "expression": "language_tag", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "ingredient_translations_ingredient_id_canonical_ingredients_id_fk": { + "name": "ingredient_translations_ingredient_id_canonical_ingredients_id_fk", + "tableFrom": "ingredient_translations", + "tableTo": "canonical_ingredients", + "columnsFrom": [ + "ingredient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipe_step_translations": { + "name": "recipe_step_translations", + "schema": "", + "columns": { + "recipe_id": { + "name": "recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "language_tag": { + "name": "language_tag", + "type": "varchar(35)", + "primaryKey": false, + "notNull": true + }, + "step_number": { + "name": "step_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "instruction": { + "name": "instruction", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tip": { + "name": "tip", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "recipe_step_translations_recipe_id_recipes_id_fk": { + "name": "recipe_step_translations_recipe_id_recipes_id_fk", + "tableFrom": "recipe_step_translations", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "recipe_step_translations_recipe_id_language_tag_step_number_pk": { + "name": "recipe_step_translations_recipe_id_language_tag_step_number_pk", + "columns": [ + "recipe_id", + "language_tag", + "step_number" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipe_translations": { + "name": "recipe_translations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "recipe_id": { + "name": "recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "language_tag": { + "name": "language_tag", + "type": "varchar(35)", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "storage_guidance": { + "name": "storage_guidance", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "translation_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'draft_ai'" + }, + "source": { + "name": "source", + "type": "translation_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'ai'" + }, + "verification": { + "name": "verification", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "recipe_translations_unique": { + "name": "recipe_translations_unique", + "columns": [ + { + "expression": "recipe_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "language_tag", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "recipe_translations_lang_idx": { + "name": "recipe_translations_lang_idx", + "columns": [ + { + "expression": "language_tag", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "recipe_translations_recipe_id_recipes_id_fk": { + "name": "recipe_translations_recipe_id_recipes_id_fk", + "tableFrom": "recipe_translations", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.unit_translations": { + "name": "unit_translations", + "schema": "", + "columns": { + "unit_code": { + "name": "unit_code", + "type": "unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "language_tag": { + "name": "language_tag", + "type": "varchar(35)", + "primaryKey": false, + "notNull": true + }, + "abbreviation": { + "name": "abbreviation", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "unit_translations_unit_code_language_tag_pk": { + "name": "unit_translations_unit_code_language_tag_pk", + "columns": [ + "unit_code", + "language_tag" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.allergen_market_rules": { + "name": "allergen_market_rules", + "schema": "", + "columns": { + "region_code": { + "name": "region_code", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + }, + "allergen": { + "name": "allergen", + "type": "allergen", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "must_highlight": { + "name": "must_highlight", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "allergen_market_rules_region_code_allergen_pk": { + "name": "allergen_market_rules_region_code_allergen_pk", + "columns": [ + "region_code", + "allergen" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.nutrition_display_profiles": { + "name": "nutrition_display_profiles", + "schema": "", + "columns": { + "region_code": { + "name": "region_code", + "type": "varchar(2)", + "primaryKey": true, + "notNull": true + }, + "energy_display": { + "name": "energy_display", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "salt_display": { + "name": "salt_display", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "energy_label_key": { + "name": "energy_label_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.meal_boxes": { + "name": "meal_boxes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recipe_id": { + "name": "recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "title_sv": { + "name": "title_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "portions": { + "name": "portions", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "portions_remaining": { + "name": "portions_remaining", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "nutrition_per_portion": { + "name": "nutrition_per_portion", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "cooked_at": { + "name": "cooked_at", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "storage_location_id": { + "name": "storage_location_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "frozen": { + "name": "frozen", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "recommended_use_by": { + "name": "recommended_use_by", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "reserved_for_user_id": { + "name": "reserved_for_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'available'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "meal_boxes_household_idx": { + "name": "meal_boxes_household_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "meal_boxes_use_by_idx": { + "name": "meal_boxes_use_by_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "recommended_use_by", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "meal_boxes_household_id_households_id_fk": { + "name": "meal_boxes_household_id_households_id_fk", + "tableFrom": "meal_boxes", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "meal_boxes_recipe_id_recipes_id_fk": { + "name": "meal_boxes_recipe_id_recipes_id_fk", + "tableFrom": "meal_boxes", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "meal_boxes_storage_location_id_storage_locations_id_fk": { + "name": "meal_boxes_storage_location_id_storage_locations_id_fk", + "tableFrom": "meal_boxes", + "tableTo": "storage_locations", + "columnsFrom": [ + "storage_location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "meal_boxes_reserved_for_user_id_users_id_fk": { + "name": "meal_boxes_reserved_for_user_id_users_id_fk", + "tableFrom": "meal_boxes", + "tableTo": "users", + "columnsFrom": [ + "reserved_for_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.meals": { + "name": "meals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "meal_type": { + "name": "meal_type", + "type": "meal_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "meal_log_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "recipe_id": { + "name": "recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "title_sv": { + "name": "title_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "portion_fraction": { + "name": "portion_fraction", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "nutrition": { + "name": "nutrition", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "nutrition_is_estimate": { + "name": "nutrition_is_estimate", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "estimate_min_kcal": { + "name": "estimate_min_kcal", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "estimate_max_kcal": { + "name": "estimate_max_kcal", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "items": { + "name": "items", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "photo_url": { + "name": "photo_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scan_job_id": { + "name": "scan_job_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "logged_at": { + "name": "logged_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "meals_user_date_idx": { + "name": "meals_user_date_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "meals_household_idx": { + "name": "meals_household_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "meals_user_id_users_id_fk": { + "name": "meals_user_id_users_id_fk", + "tableFrom": "meals", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "meals_household_id_households_id_fk": { + "name": "meals_household_id_households_id_fk", + "tableFrom": "meals", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "meals_recipe_id_recipes_id_fk": { + "name": "meals_recipe_id_recipes_id_fk", + "tableFrom": "meals", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.shopping_list_items": { + "name": "shopping_list_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "shopping_list_id": { + "name": "shopping_list_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "canonical_ingredient_id": { + "name": "canonical_ingredient_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "unit": { + "name": "unit", + "type": "unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'COUNT'" + }, + "store_section": { + "name": "store_section", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'hygien_ovrigt'" + }, + "suggested_package_size": { + "name": "suggested_package_size", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "estimated_price_minor": { + "name": "estimated_price_minor", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "checked": { + "name": "checked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "added_by_user_id": { + "name": "added_by_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "origin": { + "name": "origin", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "shopping_list_items_list_idx": { + "name": "shopping_list_items_list_idx", + "columns": [ + { + "expression": "shopping_list_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "shopping_list_items_shopping_list_id_shopping_lists_id_fk": { + "name": "shopping_list_items_shopping_list_id_shopping_lists_id_fk", + "tableFrom": "shopping_list_items", + "tableTo": "shopping_lists", + "columnsFrom": [ + "shopping_list_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "shopping_list_items_canonical_ingredient_id_canonical_ingredients_id_fk": { + "name": "shopping_list_items_canonical_ingredient_id_canonical_ingredients_id_fk", + "tableFrom": "shopping_list_items", + "tableTo": "canonical_ingredients", + "columnsFrom": [ + "canonical_ingredient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "shopping_list_items_added_by_user_id_users_id_fk": { + "name": "shopping_list_items_added_by_user_id_users_id_fk", + "tableFrom": "shopping_list_items", + "tableTo": "users", + "columnsFrom": [ + "added_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.shopping_lists": { + "name": "shopping_lists", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'Inköpslista'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "week_plan_id": { + "name": "week_plan_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "shopping_lists_household_idx": { + "name": "shopping_lists_household_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "shopping_lists_household_id_households_id_fk": { + "name": "shopping_lists_household_id_households_id_fk", + "tableFrom": "shopping_lists", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "shopping_lists_week_plan_id_week_plans_id_fk": { + "name": "shopping_lists_week_plan_id_week_plans_id_fk", + "tableFrom": "shopping_lists", + "tableTo": "week_plans", + "columnsFrom": [ + "week_plan_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.week_plan_entries": { + "name": "week_plan_entries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "week_plan_id": { + "name": "week_plan_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "meal_type": { + "name": "meal_type", + "type": "meal_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "recipe_id": { + "name": "recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "meal_box_id": { + "name": "meal_box_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "title_sv": { + "name": "title_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "portions": { + "name": "portions", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 2 + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'planned'" + }, + "reschedule_reason_sv": { + "name": "reschedule_reason_sv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "week_plan_entries_plan_idx": { + "name": "week_plan_entries_plan_idx", + "columns": [ + { + "expression": "week_plan_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "week_plan_entries_week_plan_id_week_plans_id_fk": { + "name": "week_plan_entries_week_plan_id_week_plans_id_fk", + "tableFrom": "week_plan_entries", + "tableTo": "week_plans", + "columnsFrom": [ + "week_plan_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "week_plan_entries_recipe_id_recipes_id_fk": { + "name": "week_plan_entries_recipe_id_recipes_id_fk", + "tableFrom": "week_plan_entries", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "week_plan_entries_meal_box_id_meal_boxes_id_fk": { + "name": "week_plan_entries_meal_box_id_meal_boxes_id_fk", + "tableFrom": "week_plan_entries", + "tableTo": "meal_boxes", + "columnsFrom": [ + "meal_box_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.week_plans": { + "name": "week_plans", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "week_start_date": { + "name": "week_start_date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "generated_by": { + "name": "generated_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "week_plans_household_week_idx": { + "name": "week_plans_household_week_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "week_start_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "week_plans_household_id_households_id_fk": { + "name": "week_plans_household_id_households_id_fk", + "tableFrom": "week_plans", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.price_observations": { + "name": "price_observations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "canonical_ingredient_id": { + "name": "canonical_ingredient_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "store_name": { + "name": "store_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "price_minor": { + "name": "price_minor", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "unit": { + "name": "unit", + "type": "unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "price_per_kg_minor": { + "name": "price_per_kg_minor", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "observed_at": { + "name": "observed_at", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'receipt'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "price_observations_ingredient_idx": { + "name": "price_observations_ingredient_idx", + "columns": [ + { + "expression": "canonical_ingredient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "observed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "price_observations_canonical_ingredient_id_canonical_ingredients_id_fk": { + "name": "price_observations_canonical_ingredient_id_canonical_ingredients_id_fk", + "tableFrom": "price_observations", + "tableTo": "canonical_ingredients", + "columnsFrom": [ + "canonical_ingredient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "price_observations_product_id_products_id_fk": { + "name": "price_observations_product_id_products_id_fk", + "tableFrom": "price_observations", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.receipt_lines": { + "name": "receipt_lines", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "receipt_id": { + "name": "receipt_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "raw_text": { + "name": "raw_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "normalized_name": { + "name": "normalized_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "canonical_ingredient_id": { + "name": "canonical_ingredient_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "quantity": { + "name": "quantity", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "unit": { + "name": "unit", + "type": "unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "unit_price_minor": { + "name": "unit_price_minor", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_price_minor": { + "name": "total_price_minor", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "confidence": { + "name": "confidence", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "verified_by_user": { + "name": "verified_by_user", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "added_to_inventory": { + "name": "added_to_inventory", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "receipt_lines_receipt_idx": { + "name": "receipt_lines_receipt_idx", + "columns": [ + { + "expression": "receipt_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "receipt_lines_receipt_id_receipts_id_fk": { + "name": "receipt_lines_receipt_id_receipts_id_fk", + "tableFrom": "receipt_lines", + "tableTo": "receipts", + "columnsFrom": [ + "receipt_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "receipt_lines_canonical_ingredient_id_canonical_ingredients_id_fk": { + "name": "receipt_lines_canonical_ingredient_id_canonical_ingredients_id_fk", + "tableFrom": "receipt_lines", + "tableTo": "canonical_ingredients", + "columnsFrom": [ + "canonical_ingredient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "receipt_lines_product_id_products_id_fk": { + "name": "receipt_lines_product_id_products_id_fk", + "tableFrom": "receipt_lines", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.receipts": { + "name": "receipts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "store_name": { + "name": "store_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "purchase_date": { + "name": "purchase_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "total_minor": { + "name": "total_minor", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "discount_minor": { + "name": "discount_minor", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "image_url": { + "name": "image_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scan_job_id": { + "name": "scan_job_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "receipts_household_idx": { + "name": "receipts_household_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "purchase_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "receipts_household_id_households_id_fk": { + "name": "receipts_household_id_households_id_fk", + "tableFrom": "receipts", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_corrections": { + "name": "ai_corrections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "scan_job_id": { + "name": "scan_job_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "task_type": { + "name": "task_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ai_output": { + "name": "ai_output", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "user_correction": { + "name": "user_correction", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "model_version": { + "name": "model_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prompt_version": { + "name": "prompt_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "consent_snapshot": { + "name": "consent_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "exported_to_training": { + "name": "exported_to_training", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "training_batch_id": { + "name": "training_batch_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ai_corrections_task_idx": { + "name": "ai_corrections_task_idx", + "columns": [ + { + "expression": "task_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "ai_corrections_scan_job_id_scan_jobs_id_fk": { + "name": "ai_corrections_scan_job_id_scan_jobs_id_fk", + "tableFrom": "ai_corrections", + "tableTo": "scan_jobs", + "columnsFrom": [ + "scan_job_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "ai_corrections_user_id_users_id_fk": { + "name": "ai_corrections_user_id_users_id_fk", + "tableFrom": "ai_corrections", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scan_jobs": { + "name": "scan_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "scan_type": { + "name": "scan_type", + "type": "scan_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "job_type": { + "name": "job_type", + "type": "job_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "job_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "s3_keys": { + "name": "s3_keys", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "context": { + "name": "context", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model_version": { + "name": "model_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prompt_version": { + "name": "prompt_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "latency_ms": { + "name": "latency_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cost_usd": { + "name": "cost_usd", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scan_jobs_user_idx": { + "name": "scan_jobs_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scan_jobs_status_idx": { + "name": "scan_jobs_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "scan_jobs_user_id_users_id_fk": { + "name": "scan_jobs_user_id_users_id_fk", + "tableFrom": "scan_jobs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "scan_jobs_household_id_households_id_fk": { + "name": "scan_jobs_household_id_households_id_fk", + "tableFrom": "scan_jobs", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.food_memories": { + "name": "food_memories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "recipe_id": { + "name": "recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "title_sv": { + "name": "title_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "summary_sv": { + "name": "summary_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "holiday_tag": { + "name": "holiday_tag", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "photo_url": { + "name": "photo_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stars": { + "name": "stars", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "food_memories_household_idx": { + "name": "food_memories_household_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "food_memories_holiday_idx": { + "name": "food_memories_holiday_idx", + "columns": [ + { + "expression": "holiday_tag", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "food_memories_user_id_users_id_fk": { + "name": "food_memories_user_id_users_id_fk", + "tableFrom": "food_memories", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "food_memories_household_id_households_id_fk": { + "name": "food_memories_household_id_households_id_fk", + "tableFrom": "food_memories", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "food_memories_recipe_id_recipes_id_fk": { + "name": "food_memories_recipe_id_recipes_id_fk", + "tableFrom": "food_memories", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.memory_items": { + "name": "memory_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "kind": { + "name": "kind", + "type": "memory_kind", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "summary_sv": { + "name": "summary_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "origin": { + "name": "origin", + "type": "signal_origin", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "confidence": { + "name": "confidence", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0.5 + }, + "verified_by_user": { + "name": "verified_by_user", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "paused": { + "name": "paused", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "memory_items_user_idx": { + "name": "memory_items_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "memory_items_household_idx": { + "name": "memory_items_household_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "memory_items_key_idx": { + "name": "memory_items_key_idx", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "memory_items_user_id_users_id_fk": { + "name": "memory_items_user_id_users_id_fk", + "tableFrom": "memory_items", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "memory_items_household_id_households_id_fk": { + "name": "memory_items_household_id_households_id_fk", + "tableFrom": "memory_items", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.taste_signals": { + "name": "taste_signals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "axis": { + "name": "axis", + "type": "taste_axis", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "direction": { + "name": "direction", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "strength": { + "name": "strength", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0.5 + }, + "origin": { + "name": "origin", + "type": "signal_origin", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "ref_recipe_id": { + "name": "ref_recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "taste_signals_user_idx": { + "name": "taste_signals_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "axis", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "taste_signals_user_id_users_id_fk": { + "name": "taste_signals_user_id_users_id_fk", + "tableFrom": "taste_signals", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "taste_signals_ref_recipe_id_recipes_id_fk": { + "name": "taste_signals_ref_recipe_id_recipes_id_fk", + "tableFrom": "taste_signals", + "tableTo": "recipes", + "columnsFrom": [ + "ref_recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_events": { + "name": "season_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name_sv": { + "name": "name_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "market": { + "name": "market", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'SE'" + }, + "date_rule": { + "name": "date_rule", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "lead_days": { + "name": "lead_days", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 7 + }, + "food_tags": { + "name": "food_tags", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "recipe_slugs": { + "name": "recipe_slugs", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "season_events_slug_market_unique": { + "name": "season_events_slug_market_unique", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "market", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_usage_counters": { + "name": "ai_usage_counters", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "month": { + "name": "month", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ai_scans": { + "name": "ai_scans", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "ai_tokens_in": { + "name": "ai_tokens_in", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "ai_tokens_out": { + "name": "ai_tokens_out", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ai_usage_user_month_unique": { + "name": "ai_usage_user_month_unique", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "month", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "ai_usage_counters_user_id_users_id_fk": { + "name": "ai_usage_counters_user_id_users_id_fk", + "tableFrom": "ai_usage_counters", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.store_notifications": { + "name": "store_notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "subscription_provider", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "notification_type": { + "name": "notification_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "raw_payload": { + "name": "raw_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "signature_verified": { + "name": "signature_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "processed": { + "name": "processed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "store_notifications_pending_idx": { + "name": "store_notifications_pending_idx", + "columns": [ + { + "expression": "processed", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.store_transactions": { + "name": "store_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "subscription_provider", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "transaction_id": { + "name": "transaction_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "original_transaction_id": { + "name": "original_transaction_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "product_id": { + "name": "product_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "raw_payload": { + "name": "raw_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "store_transactions_unique": { + "name": "store_transactions_unique", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "transaction_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "store_transactions_user_id_users_id_fk": { + "name": "store_transactions_user_id_users_id_fk", + "tableFrom": "store_transactions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.subscription_events": { + "name": "subscription_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "subscription_id": { + "name": "subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "subscription_events_sub_idx": { + "name": "subscription_events_sub_idx", + "columns": [ + { + "expression": "subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "subscription_events_subscription_id_subscriptions_id_fk": { + "name": "subscription_events_subscription_id_subscriptions_id_fk", + "tableFrom": "subscription_events", + "tableTo": "subscriptions", + "columnsFrom": [ + "subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "subscription_events_user_id_users_id_fk": { + "name": "subscription_events_user_id_users_id_fk", + "tableFrom": "subscription_events", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.subscriptions": { + "name": "subscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "subscription_provider", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "product_id": { + "name": "product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plan": { + "name": "plan", + "type": "subscription_plan", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "original_transaction_id": { + "name": "original_transaction_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "subscription_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "purchased_at": { + "name": "purchased_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "grace_period_expires_at": { + "name": "grace_period_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "canceled_at": { + "name": "canceled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_verified_at": { + "name": "last_verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "subscriptions_user_idx": { + "name": "subscriptions_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "subscriptions_original_tx_unique": { + "name": "subscriptions_original_tx_unique", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "original_transaction_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"subscriptions\".\"original_transaction_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "subscriptions_user_id_users_id_fk": { + "name": "subscriptions_user_id_users_id_fk", + "tableFrom": "subscriptions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "subscriptions_household_id_households_id_fk": { + "name": "subscriptions_household_id_households_id_fk", + "tableFrom": "subscriptions", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.trials": { + "name": "trials", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ends_at": { + "name": "ends_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "trials_user_id_users_id_fk": { + "name": "trials_user_id_users_id_fk", + "tableFrom": "trials", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.analytics_daily_snapshots": { + "name": "analytics_daily_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "date": { + "name": "date", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "event_name": { + "name": "event_name", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "count": { + "name": "count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "unique_sessions": { + "name": "unique_sessions", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "properties_fingerprint": { + "name": "properties_fingerprint", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "pa_daily_household_date_idx": { + "name": "pa_daily_household_date_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pa_daily_date_event_idx": { + "name": "pa_daily_date_event_idx", + "columns": [ + { + "expression": "date", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "event_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "analytics_daily_snapshots_household_id_households_id_fk": { + "name": "analytics_daily_snapshots_household_id_households_id_fk", + "tableFrom": "analytics_daily_snapshots", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_analytics_events": { + "name": "product_analytics_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "received_at": { + "name": "received_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "event_name": { + "name": "event_name", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "anonymous_id": { + "name": "anonymous_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "app_version": { + "name": "app_version", + "type": "varchar(32)", + "primaryKey": false, + "notNull": false + }, + "platform": { + "name": "platform", + "type": "varchar(16)", + "primaryKey": false, + "notNull": false + }, + "locale": { + "name": "locale", + "type": "varchar(16)", + "primaryKey": false, + "notNull": false + }, + "experiment_variant": { + "name": "experiment_variant", + "type": "varchar(128)", + "primaryKey": false, + "notNull": false + }, + "properties": { + "name": "properties", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + } + }, + "indexes": { + "pa_events_name_occurred_idx": { + "name": "pa_events_name_occurred_idx", + "columns": [ + { + "expression": "event_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pa_events_household_occurred_idx": { + "name": "pa_events_household_occurred_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pa_events_user_occurred_idx": { + "name": "pa_events_user_occurred_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pa_events_session_idx": { + "name": "pa_events_session_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pa_events_received_idx": { + "name": "pa_events_received_idx", + "columns": [ + { + "expression": "received_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_analytics_events_user_id_users_id_fk": { + "name": "product_analytics_events_user_id_users_id_fk", + "tableFrom": "product_analytics_events", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "product_analytics_events_household_id_households_id_fk": { + "name": "product_analytics_events_household_id_households_id_fk", + "tableFrom": "product_analytics_events", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_eval_cases": { + "name": "ai_eval_cases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "task_type": { + "name": "task_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name_sv": { + "name": "name_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "input_ref": { + "name": "input_ref", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "expected_output": { + "name": "expected_output", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'standard'" + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_eval_runs": { + "name": "ai_eval_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "task_type": { + "name": "task_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model_version": { + "name": "model_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prompt_version": { + "name": "prompt_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metrics": { + "name": "metrics", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "passed": { + "name": "passed", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ai_eval_runs_task_idx": { + "name": "ai_eval_runs_task_idx", + "columns": [ + { + "expression": "task_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.audit_logs": { + "name": "audit_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "actor_type": { + "name": "actor_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_type": { + "name": "target_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_id": { + "name": "target_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "ip": { + "name": "ip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "correlation_id": { + "name": "correlation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "audit_logs_actor_idx": { + "name": "audit_logs_actor_idx", + "columns": [ + { + "expression": "actor_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_logs_action_idx": { + "name": "audit_logs_action_idx", + "columns": [ + { + "expression": "action", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.creator_follows": { + "name": "creator_follows", + "schema": "", + "columns": { + "follower_user_id": { + "name": "follower_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "creator_user_id": { + "name": "creator_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "creator_follows_follower_user_id_users_id_fk": { + "name": "creator_follows_follower_user_id_users_id_fk", + "tableFrom": "creator_follows", + "tableTo": "users", + "columnsFrom": [ + "follower_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "creator_follows_creator_user_id_users_id_fk": { + "name": "creator_follows_creator_user_id_users_id_fk", + "tableFrom": "creator_follows", + "tableTo": "users", + "columnsFrom": [ + "creator_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "creator_follows_follower_user_id_creator_user_id_pk": { + "name": "creator_follows_follower_user_id_creator_user_id_pk", + "columns": [ + "follower_user_id", + "creator_user_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.creator_stats": { + "name": "creator_stats", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "visibility": { + "name": "visibility", + "type": "profile_visibility", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'private'" + }, + "level": { + "name": "level", + "type": "creator_level", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'beginner'" + }, + "published_recipes": { + "name": "published_recipes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "followers": { + "name": "followers", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_cooks": { + "name": "total_cooks", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_favorites": { + "name": "total_favorites", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "average_rating": { + "name": "average_rating", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "verified_recipes": { + "name": "verified_recipes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "badges": { + "name": "badges", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "creator_stats_user_id_users_id_fk": { + "name": "creator_stats_user_id_users_id_fk", + "tableFrom": "creator_stats", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.domain_events": { + "name": "domain_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "type": { + "name": "type", + "type": "event_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "correlation_id": { + "name": "correlation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "published_at": { + "name": "published_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "domain_events_unpublished_idx": { + "name": "domain_events_unpublished_idx", + "columns": [ + { + "expression": "published_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "domain_events_type_idx": { + "name": "domain_events_type_idx", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "domain_events_household_idx": { + "name": "domain_events_household_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.feature_flags": { + "name": "feature_flags", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "description_sv": { + "name": "description_sv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rollout_percent": { + "name": "rollout_percent", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 100 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.idempotency_keys": { + "name": "idempotency_keys", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "response_status": { + "name": "response_status", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "response_body": { + "name": "response_body", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "idempotency_keys_user_id_users_id_fk": { + "name": "idempotency_keys_user_id_users_id_fk", + "tableFrom": "idempotency_keys", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "idempotency_keys_user_id_key_endpoint_pk": { + "name": "idempotency_keys_user_id_key_endpoint_pk", + "columns": [ + "user_id", + "key", + "endpoint" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notifications": { + "name": "notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "notification_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "title_sv": { + "name": "title_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body_sv": { + "name": "body_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "template_key": { + "name": "template_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "variables": { + "name": "variables", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "locale": { + "name": "locale", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scheduled_for": { + "name": "scheduled_for", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "sent_at": { + "name": "sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "read_at": { + "name": "read_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "notifications_user_idx": { + "name": "notifications_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "notifications_user_id_users_id_fk": { + "name": "notifications_user_id_users_id_fk", + "tableFrom": "notifications", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.push_tokens": { + "name": "push_tokens", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "push_tokens_user_id_users_id_fk": { + "name": "push_tokens_user_id_users_id_fk", + "tableFrom": "push_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "push_tokens_user_id_token_pk": { + "name": "push_tokens_user_id_token_pk", + "columns": [ + "user_id", + "token" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.waste_summaries": { + "name": "waste_summaries", + "schema": "", + "columns": { + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "week_start_date": { + "name": "week_start_date", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "discarded_count": { + "name": "discarded_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "discarded_value_minor": { + "name": "discarded_value_minor", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "waste_summaries_household_id_households_id_fk": { + "name": "waste_summaries_household_id_households_id_fk", + "tableFrom": "waste_summaries", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "waste_summaries_household_id_week_start_date_pk": { + "name": "waste_summaries_household_id_week_start_date_pk", + "columns": [ + "household_id", + "week_start_date" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.release_gates": { + "name": "release_gates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "gate_key": { + "name": "gate_key", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "release_gate_category", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "name_sv": { + "name": "name_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name_en": { + "name": "name_en", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description_sv": { + "name": "description_sv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description_en": { + "name": "description_en", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_value": { + "name": "target_value", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "comparison": { + "name": "comparison", + "type": "release_gate_comparison", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "evaluation_window_days": { + "name": "evaluation_window_days", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 7 + }, + "last_value": { + "name": "last_value", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "last_evaluated_at": { + "name": "last_evaluated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "release_gate_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "blocking": { + "name": "blocking", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "release_gates_category_idx": { + "name": "release_gates_category_idx", + "columns": [ + { + "expression": "category", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "release_gates_status_idx": { + "name": "release_gates_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "release_gates_gate_key_unique": { + "name": "release_gates_gate_key_unique", + "nullsNotDistinct": false, + "columns": [ + "gate_key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.activity_level": { + "name": "activity_level", + "schema": "public", + "values": [ + "sedentary", + "light", + "moderate", + "active", + "very_active" + ] + }, + "public.allergen": { + "name": "allergen", + "schema": "public", + "values": [ + "gluten", + "crustaceans", + "eggs", + "fish", + "peanuts", + "soy", + "milk", + "tree_nuts", + "celery", + "mustard", + "sesame", + "sulphites", + "lupin", + "molluscs" + ] + }, + "public.consent_kind": { + "name": "consent_kind", + "schema": "public", + "values": [ + "personalization", + "anonymized_improvement", + "image_training", + "health_integration", + "location_weather", + "push_notifications", + "product_analytics" + ] + }, + "public.consent_status": { + "name": "consent_status", + "schema": "public", + "values": [ + "granted", + "denied", + "revoked" + ] + }, + "public.cooking_method": { + "name": "cooking_method", + "schema": "public", + "values": [ + "stovetop", + "oven", + "grill", + "airfryer", + "wok", + "slow_cooker", + "sous_vide", + "microwave", + "no_cook", + "pressure_cooker", + "deep_fry", + "steam" + ] + }, + "public.creator_level": { + "name": "creator_level", + "schema": "public", + "values": [ + "beginner", + "sous_chef", + "chef", + "master_chef", + "legend" + ] + }, + "public.cuisine": { + "name": "cuisine", + "schema": "public", + "values": [ + "swedish", + "nordic", + "italian", + "french", + "spanish", + "greek", + "thai", + "chinese", + "japanese", + "korean", + "vietnamese", + "indian", + "mexican", + "american", + "turkish", + "lebanese", + "moroccan", + "middle_eastern", + "international" + ] + }, + "public.date_kind": { + "name": "date_kind", + "schema": "public", + "values": [ + "best_before", + "use_by" + ] + }, + "public.diet_pattern": { + "name": "diet_pattern", + "schema": "public", + "values": [ + "omnivore", + "flexitarian", + "pescatarian", + "vegetarian", + "vegan", + "low_carb", + "keto", + "high_protein", + "mediterranean" + ] + }, + "public.event_type": { + "name": "event_type", + "schema": "public", + "values": [ + "PRODUCT_ADDED", + "PRODUCT_UPDATED", + "PRODUCT_CONSUMED", + "PRODUCT_DISCARDED", + "RECIPE_COOKED", + "RECIPE_RATED", + "RECIPE_CREATED", + "RECIPE_FORKED", + "MEAL_LOGGED", + "MEAL_PHOTO_ANALYZED", + "HOUSEHOLD_MEMBER_ADDED", + "SUBSCRIPTION_STARTED", + "SUBSCRIPTION_CHANGED", + "AI_CORRECTED", + "MEMORY_UPDATED", + "SHOPPING_COMPLETED", + "WEEK_PLAN_UPDATED", + "MEAL_BOX_CREATED", + "MEAL_BOX_CONSUMED" + ] + }, + "public.expiry_status": { + "name": "expiry_status", + "schema": "public", + "values": [ + "fresh", + "use_soon", + "expiring", + "expired", + "unknown" + ] + }, + "public.goal_type": { + "name": "goal_type", + "schema": "public", + "values": [ + "lose_weight", + "gain_weight", + "build_muscle", + "maintain_weight", + "more_protein", + "less_fat", + "more_fiber", + "more_variety", + "less_waste", + "lower_cost", + "cook_more" + ] + }, + "public.household_role": { + "name": "household_role", + "schema": "public", + "values": [ + "owner", + "adult", + "member", + "child" + ] + }, + "public.inventory_source": { + "name": "inventory_source", + "schema": "public", + "values": [ + "fridge_photo", + "freezer_photo", + "pantry_photo", + "ingredient_photo", + "barcode", + "receipt", + "digital_receipt", + "label_photo", + "manual_search", + "free_text", + "voice", + "cooked_recipe", + "connector", + "seed" + ] + }, + "public.inventory_transaction_type": { + "name": "inventory_transaction_type", + "schema": "public", + "values": [ + "purchase", + "consume", + "discard", + "adjust", + "cook_use", + "leftover_created", + "leftover_consumed", + "correction", + "freeze", + "thaw", + "move" + ] + }, + "public.job_status": { + "name": "job_status", + "schema": "public", + "values": [ + "queued", + "running", + "awaiting_confirmation", + "completed", + "failed", + "canceled" + ] + }, + "public.job_type": { + "name": "job_type", + "schema": "public", + "values": [ + "ANALYZE_FRIDGE_IMAGE", + "ANALYZE_PANTRY_IMAGE", + "ANALYZE_MEAL_IMAGE", + "READ_RECEIPT", + "READ_NUTRITION_LABEL", + "READ_EXPIRY_DATE", + "NORMALIZE_PRODUCTS", + "DEDUPLICATE_INVENTORY", + "CALCULATE_NUTRITION", + "GENERATE_RECIPE_OPTIONS", + "RANK_RECIPES", + "UPDATE_USER_MEMORY", + "GENERATE_WEEK_PLAN", + "SEND_EXPIRY_NOTIFICATION", + "VERIFY_SUBSCRIPTION", + "PROCESS_STORE_NOTIFICATION", + "BUILD_TRAINING_SAMPLE", + "RUN_AI_EVALUATION" + ] + }, + "public.meal_log_source": { + "name": "meal_log_source", + "schema": "public", + "values": [ + "cooked_recipe", + "plate_photo", + "barcode", + "product", + "free_text", + "voice", + "previous_meal", + "meal_box", + "manual" + ] + }, + "public.meal_type": { + "name": "meal_type", + "schema": "public", + "values": [ + "breakfast", + "lunch", + "dinner", + "snack", + "dessert", + "starter", + "buffet", + "party" + ] + }, + "public.memory_kind": { + "name": "memory_kind", + "schema": "public", + "values": [ + "structured_fact", + "event", + "semantic", + "profile_summary", + "recipe_memory" + ] + }, + "public.notification_type": { + "name": "notification_type", + "schema": "public", + "values": [ + "expiry_warning", + "meal_box_reminder", + "quick_dinner_suggestion", + "holiday_upcoming", + "creator_new_recipe", + "week_plan_change", + "pantry_forecast", + "subscription_status" + ] + }, + "public.precision_mode": { + "name": "precision_mode", + "schema": "public", + "values": [ + "simple", + "exact" + ] + }, + "public.profile_visibility": { + "name": "profile_visibility", + "schema": "public", + "values": [ + "private", + "friends", + "national", + "global" + ] + }, + "public.recipe_difficulty": { + "name": "recipe_difficulty", + "schema": "public", + "values": [ + "beginner", + "easy", + "medium", + "advanced", + "expert" + ] + }, + "public.recipe_similarity_class": { + "name": "recipe_similarity_class", + "schema": "public", + "values": [ + "duplicate", + "variant", + "inspired", + "independent" + ] + }, + "public.recipe_source_type": { + "name": "recipe_source_type", + "schema": "public", + "values": [ + "own_editorial", + "ai_assisted_reviewed", + "open_license", + "public_domain", + "licensed_database", + "creator_agreement", + "user_generated" + ] + }, + "public.recipe_status": { + "name": "recipe_status", + "schema": "public", + "values": [ + "draft", + "submitted", + "ai_checked", + "in_moderation", + "published", + "rejected", + "archived" + ] + }, + "public.recipe_variant_type": { + "name": "recipe_variant_type", + "schema": "public", + "values": [ + "standard", + "high_protein", + "low_calorie", + "low_fat", + "vegetarian", + "vegan", + "gluten_free", + "lactose_free", + "kid_friendly", + "airfryer", + "budget" + ] + }, + "public.recipe_verification_status": { + "name": "recipe_verification_status", + "schema": "public", + "values": [ + "unverified", + "community", + "verified", + "editorial" + ] + }, + "public.religious_rule": { + "name": "religious_rule", + "schema": "public", + "values": [ + "none", + "halal", + "kosher", + "hindu_no_beef", + "buddhist_vegetarian" + ] + }, + "public.scan_type": { + "name": "scan_type", + "schema": "public", + "values": [ + "fridge", + "freezer", + "pantry", + "ingredients", + "plate", + "receipt", + "barcode", + "expiry_date", + "nutrition_label", + "product_package" + ] + }, + "public.sex": { + "name": "sex", + "schema": "public", + "values": [ + "female", + "male", + "unspecified" + ] + }, + "public.signal_origin": { + "name": "signal_origin", + "schema": "public", + "values": [ + "user_stated", + "observed", + "ai_inferred" + ] + }, + "public.storage_location_type": { + "name": "storage_location_type", + "schema": "public", + "values": [ + "fridge", + "freezer", + "pantry", + "garage_freezer", + "wine_fridge", + "cellar", + "meal_box", + "custom" + ] + }, + "public.subscription_plan": { + "name": "subscription_plan", + "schema": "public", + "values": [ + "free", + "household", + "family", + "large_household" + ] + }, + "public.subscription_provider": { + "name": "subscription_provider", + "schema": "public", + "values": [ + "apple", + "google", + "promo", + "none" + ] + }, + "public.subscription_status": { + "name": "subscription_status", + "schema": "public", + "values": [ + "trial", + "active", + "in_grace", + "on_hold", + "paused", + "canceled", + "expired" + ] + }, + "public.taste_axis": { + "name": "taste_axis", + "schema": "public", + "values": [ + "spice", + "salt", + "acid", + "creaminess", + "garlic", + "sweetness", + "herbs", + "umami" + ] + }, + "public.unit": { + "name": "unit", + "schema": "public", + "values": [ + "GRAM", + "KILOGRAM", + "MILLILITER", + "DECILITER", + "LITER", + "TEASPOON", + "TABLESPOON", + "CUP_US", + "FLUID_OUNCE_US", + "OUNCE", + "POUND", + "COUNT", + "PORTION", + "PINCH", + "SLICE", + "CLOVE", + "CAN", + "PACKAGE" + ] + }, + "public.user_role": { + "name": "user_role", + "schema": "public", + "values": [ + "user", + "moderator", + "admin" + ] + }, + "public.verification_status": { + "name": "verification_status", + "schema": "public", + "values": [ + "unverified", + "user_verified", + "editorially_verified" + ] + }, + "public.measurement_system": { + "name": "measurement_system", + "schema": "public", + "values": [ + "METRIC", + "US_CUSTOMARY", + "MIXED" + ] + }, + "public.temperature_unit": { + "name": "temperature_unit", + "schema": "public", + "values": [ + "CELSIUS", + "FAHRENHEIT" + ] + }, + "public.translation_source": { + "name": "translation_source", + "schema": "public", + "values": [ + "seed", + "ai", + "human" + ] + }, + "public.translation_status": { + "name": "translation_status", + "schema": "public", + "values": [ + "draft_ai", + "in_review", + "published" + ] + }, + "public.release_gate_category": { + "name": "release_gate_category", + "schema": "public", + "values": [ + "product", + "quality", + "retention", + "economy" + ] + }, + "public.release_gate_comparison": { + "name": "release_gate_comparison", + "schema": "public", + "values": [ + "gte", + "lte", + "eq" + ] + }, + "public.release_gate_status": { + "name": "release_gate_status", + "schema": "public", + "values": [ + "pending", + "passed", + "failed", + "blocked", + "not_measurable" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/infrastructure/migrations/meta/0009_snapshot.json b/infrastructure/migrations/meta/0009_snapshot.json new file mode 100644 index 0000000..d2a7e55 --- /dev/null +++ b/infrastructure/migrations/meta/0009_snapshot.json @@ -0,0 +1,8992 @@ +{ + "id": "a6520529-bbb4-405d-8310-a8b8f0809e35", + "prevId": "b26aaf39-2905-4816-9575-ac7e429c3a28", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.admin_totp": { + "name": "admin_totp", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "secret_base32": { + "name": "secret_base32", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled_at": { + "name": "enabled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_used_step": { + "name": "last_used_step", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "admin_totp_user_id_users_id_fk": { + "name": "admin_totp_user_id_users_id_fk", + "tableFrom": "admin_totp", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email_verification_tokens": { + "name": "email_verification_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "used_at": { + "name": "used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "email_verification_tokens_user_idx": { + "name": "email_verification_tokens_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "email_verification_tokens_hash_unique": { + "name": "email_verification_tokens_hash_unique", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "email_verification_tokens_user_id_users_id_fk": { + "name": "email_verification_tokens_user_id_users_id_fk", + "tableFrom": "email_verification_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.password_reset_tokens": { + "name": "password_reset_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "used_at": { + "name": "used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "password_reset_tokens_user_idx": { + "name": "password_reset_tokens_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "password_reset_tokens_hash_unique": { + "name": "password_reset_tokens_hash_unique", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "password_reset_tokens_user_id_users_id_fk": { + "name": "password_reset_tokens_user_id_users_id_fk", + "tableFrom": "password_reset_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.refresh_tokens": { + "name": "refresh_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "family_id": { + "name": "family_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "replaced_by_token_id": { + "name": "replaced_by_token_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ip": { + "name": "ip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "refresh_tokens_user_idx": { + "name": "refresh_tokens_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "refresh_tokens_hash_unique": { + "name": "refresh_tokens_hash_unique", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "refresh_tokens_user_id_users_id_fk": { + "name": "refresh_tokens_user_id_users_id_fk", + "tableFrom": "refresh_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_consents": { + "name": "user_consents", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "consent_kind", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "consent_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "granted_at": { + "name": "granted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_consents_user_id_users_id_fk": { + "name": "user_consents_user_id_users_id_fk", + "tableFrom": "user_consents", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_consents_user_id_kind_pk": { + "name": "user_consents_user_id_kind_pk", + "columns": [ + "user_id", + "kind" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_credentials": { + "name": "user_credentials", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "password_hash": { + "name": "password_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password_updated_at": { + "name": "password_updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_credentials_user_id_users_id_fk": { + "name": "user_credentials_user_id_users_id_fk", + "tableFrom": "user_credentials", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_health_profiles": { + "name": "user_health_profiles", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "birth_year": { + "name": "birth_year", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "sex": { + "name": "sex", + "type": "sex", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "height_cm": { + "name": "height_cm", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "weight_kg": { + "name": "weight_kg", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "target_weight_kg": { + "name": "target_weight_kg", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "activity_level": { + "name": "activity_level", + "type": "activity_level", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'moderate'" + }, + "training_sessions_per_week": { + "name": "training_sessions_per_week", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "training_types": { + "name": "training_types", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_health_profiles_user_id_users_id_fk": { + "name": "user_health_profiles_user_id_users_id_fk", + "tableFrom": "user_health_profiles", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_preferences": { + "name": "user_preferences", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "primary_goal": { + "name": "primary_goal", + "type": "goal_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "goals": { + "name": "goals", + "type": "goal_type[]", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "diet_pattern": { + "name": "diet_pattern", + "type": "diet_pattern", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'omnivore'" + }, + "religious_rule": { + "name": "religious_rule", + "type": "religious_rule", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "allergens": { + "name": "allergens", + "type": "allergen[]", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "intolerances": { + "name": "intolerances", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "avoid_ingredient_ids": { + "name": "avoid_ingredient_ids", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "favorite_cuisines": { + "name": "favorite_cuisines", + "type": "cuisine[]", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "disliked_dishes": { + "name": "disliked_dishes", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "spice_level_max": { + "name": "spice_level_max", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 3 + }, + "weekly_budget_minor": { + "name": "weekly_budget_minor", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "max_cooking_minutes_weekday": { + "name": "max_cooking_minutes_weekday", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "equipment": { + "name": "equipment", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "default_portions": { + "name": "default_portions", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 2 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_preferences_user_id_users_id_fk": { + "name": "user_preferences_user_id_users_id_fk", + "tableFrom": "user_preferences", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified_at": { + "name": "email_verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "user_role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "locale": { + "name": "locale", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'sv-SE'" + }, + "precision_mode": { + "name": "precision_mode", + "type": "precision_mode", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'simple'" + }, + "onboarding_completed": { + "name": "onboarding_completed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "onboarding_step": { + "name": "onboarding_step", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'a'" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "users_email_unique": { + "name": "users_email_unique", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_locale_preferences": { + "name": "user_locale_preferences", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "language_tag": { + "name": "language_tag", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'sv-SE'" + }, + "region_code": { + "name": "region_code", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'SE'" + }, + "time_zone": { + "name": "time_zone", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'Europe/Stockholm'" + }, + "measurement_system": { + "name": "measurement_system", + "type": "measurement_system", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'METRIC'" + }, + "temperature_unit": { + "name": "temperature_unit", + "type": "temperature_unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'CELSIUS'" + }, + "currency_code": { + "name": "currency_code", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'SEK'" + }, + "first_day_of_week": { + "name": "first_day_of_week", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "use_24_hour_time": { + "name": "use_24_hour_time", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_locale_preferences_user_id_users_id_fk": { + "name": "user_locale_preferences_user_id_users_id_fk", + "tableFrom": "user_locale_preferences", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.household_members": { + "name": "household_members", + "schema": "", + "columns": { + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "household_role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "portion_factor": { + "name": "portion_factor", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "joined_at": { + "name": "joined_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "household_members_user_idx": { + "name": "household_members_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "household_members_household_id_households_id_fk": { + "name": "household_members_household_id_households_id_fk", + "tableFrom": "household_members", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "household_members_user_id_users_id_fk": { + "name": "household_members_user_id_users_id_fk", + "tableFrom": "household_members", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "household_members_household_id_user_id_pk": { + "name": "household_members_household_id_user_id_pk", + "columns": [ + "household_id", + "user_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.households": { + "name": "households", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "invite_code": { + "name": "invite_code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "weekly_budget_minor": { + "name": "weekly_budget_minor", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "first_scan_completed_at": { + "name": "first_scan_completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "fifth_item_confirmed_at": { + "name": "fifth_item_confirmed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "first_recipe_recommendation_viewed_at": { + "name": "first_recipe_recommendation_viewed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "first_recipe_saved_or_started_at": { + "name": "first_recipe_saved_or_started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "activated_at": { + "name": "activated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "first_cooking_session_completed_at": { + "name": "first_cooking_session_completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "inventory_updated_after_cooking_at": { + "name": "inventory_updated_after_cooking_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "value_confirmed_at": { + "name": "value_confirmed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "currency_code": { + "name": "currency_code", + "type": "varchar(3)", + "primaryKey": false, + "notNull": true, + "default": "'SEK'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "households_invite_code_unique": { + "name": "households_invite_code_unique", + "columns": [ + { + "expression": "invite_code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.storage_locations": { + "name": "storage_locations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "storage_location_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sublocations": { + "name": "sublocations", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "storage_locations_household_idx": { + "name": "storage_locations_household_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "storage_locations_household_id_households_id_fk": { + "name": "storage_locations_household_id_households_id_fk", + "tableFrom": "storage_locations", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.canonical_ingredients": { + "name": "canonical_ingredients", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name_sv": { + "name": "name_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name_en": { + "name": "name_en", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "aliases": { + "name": "aliases", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "default_unit": { + "name": "default_unit", + "type": "unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "density_g_per_ml": { + "name": "density_g_per_ml", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "grams_per_piece": { + "name": "grams_per_piece", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "allergens": { + "name": "allergens", + "type": "allergen[]", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "is_vegan": { + "name": "is_vegan", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_vegetarian": { + "name": "is_vegetarian", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "contains_gluten": { + "name": "contains_gluten", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "contains_lactose": { + "name": "contains_lactose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_pork": { + "name": "is_pork", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_beef": { + "name": "is_beef", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_alcohol": { + "name": "is_alcohol", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "nutrition_per_100": { + "name": "nutrition_per_100", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "nutrition_provenance": { + "name": "nutrition_provenance", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "peak_seasons": { + "name": "peak_seasons", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "shelf_life_guidance": { + "name": "shelf_life_guidance", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "default_price_minor_per_kg": { + "name": "default_price_minor_per_kg", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "canonical_ingredients_category_idx": { + "name": "canonical_ingredients_category_idx", + "columns": [ + { + "expression": "category", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.substitutions": { + "name": "substitutions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "from_ingredient_id": { + "name": "from_ingredient_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "to_ingredient_id": { + "name": "to_ingredient_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ratio": { + "name": "ratio", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "instructions_sv": { + "name": "instructions_sv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "best_for": { + "name": "best_for", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "not_recommended_for": { + "name": "not_recommended_for", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "flavor_impact_sv": { + "name": "flavor_impact_sv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "texture_impact_sv": { + "name": "texture_impact_sv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "substitutions_from_idx": { + "name": "substitutions_from_idx", + "columns": [ + { + "expression": "from_ingredient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "substitutions_pair_unique": { + "name": "substitutions_pair_unique", + "columns": [ + { + "expression": "from_ingredient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "to_ingredient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "substitutions_from_ingredient_id_canonical_ingredients_id_fk": { + "name": "substitutions_from_ingredient_id_canonical_ingredients_id_fk", + "tableFrom": "substitutions", + "tableTo": "canonical_ingredients", + "columnsFrom": [ + "from_ingredient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "substitutions_to_ingredient_id_canonical_ingredients_id_fk": { + "name": "substitutions_to_ingredient_id_canonical_ingredients_id_fk", + "tableFrom": "substitutions", + "tableTo": "canonical_ingredients", + "columnsFrom": [ + "to_ingredient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.products": { + "name": "products", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "gtin": { + "name": "gtin", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "brand": { + "name": "brand", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "canonical_ingredient_id": { + "name": "canonical_ingredient_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "package_size_value": { + "name": "package_size_value", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "package_size_unit": { + "name": "package_size_unit", + "type": "unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "ingredients_text": { + "name": "ingredients_text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "allergens": { + "name": "allergens", + "type": "allergen[]", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "may_contain_allergens": { + "name": "may_contain_allergens", + "type": "allergen[]", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "nutrition": { + "name": "nutrition", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "image_urls": { + "name": "image_urls", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'sv'" + }, + "market": { + "name": "market", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'SE'" + }, + "data_source": { + "name": "data_source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "verification_status": { + "name": "verification_status", + "type": "verification_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'unverified'" + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "valid_from": { + "name": "valid_from", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "valid_to": { + "name": "valid_to", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "products_gtin_version_unique": { + "name": "products_gtin_version_unique", + "columns": [ + { + "expression": "gtin", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"products\".\"gtin\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "products_gtin_current_idx": { + "name": "products_gtin_current_idx", + "columns": [ + { + "expression": "gtin", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"products\".\"valid_to\" IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "products_canonical_idx": { + "name": "products_canonical_idx", + "columns": [ + { + "expression": "canonical_ingredient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "products_canonical_ingredient_id_canonical_ingredients_id_fk": { + "name": "products_canonical_ingredient_id_canonical_ingredients_id_fk", + "tableFrom": "products", + "tableTo": "canonical_ingredients", + "columnsFrom": [ + "canonical_ingredient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.inventory_items": { + "name": "inventory_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "canonical_ingredient_id": { + "name": "canonical_ingredient_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "brand": { + "name": "brand", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "quantity": { + "name": "quantity", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "unit": { + "name": "unit", + "type": "unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "storage_location_id": { + "name": "storage_location_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sublocation": { + "name": "sublocation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "purchased_at": { + "name": "purchased_at", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "opened_at": { + "name": "opened_at", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "best_before_date": { + "name": "best_before_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "use_by_date": { + "name": "use_by_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "date_kind": { + "name": "date_kind", + "type": "date_kind", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "frozen_at": { + "name": "frozen_at", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "thawed_at": { + "name": "thawed_at", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "price_minor": { + "name": "price_minor", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "nutrition_per_100": { + "name": "nutrition_per_100", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "inventory_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "confidence": { + "name": "confidence", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "verified_by_user": { + "name": "verified_by_user", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "last_verified_at": { + "name": "last_verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "expiry_status": { + "name": "expiry_status", + "type": "expiry_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'unknown'" + }, + "trust_state": { + "name": "trust_state", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true, + "default": "'unverified'" + }, + "depleted_at": { + "name": "depleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "model_version": { + "name": "model_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prompt_version": { + "name": "prompt_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "inventory_items_household_idx": { + "name": "inventory_items_household_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "inventory_items_household_bb_idx": { + "name": "inventory_items_household_bb_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "best_before_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "inventory_items_location_idx": { + "name": "inventory_items_location_idx", + "columns": [ + { + "expression": "storage_location_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "inventory_items_canonical_idx": { + "name": "inventory_items_canonical_idx", + "columns": [ + { + "expression": "canonical_ingredient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "inventory_items_household_id_households_id_fk": { + "name": "inventory_items_household_id_households_id_fk", + "tableFrom": "inventory_items", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "inventory_items_canonical_ingredient_id_canonical_ingredients_id_fk": { + "name": "inventory_items_canonical_ingredient_id_canonical_ingredients_id_fk", + "tableFrom": "inventory_items", + "tableTo": "canonical_ingredients", + "columnsFrom": [ + "canonical_ingredient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inventory_items_product_id_products_id_fk": { + "name": "inventory_items_product_id_products_id_fk", + "tableFrom": "inventory_items", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "inventory_items_storage_location_id_storage_locations_id_fk": { + "name": "inventory_items_storage_location_id_storage_locations_id_fk", + "tableFrom": "inventory_items", + "tableTo": "storage_locations", + "columnsFrom": [ + "storage_location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.inventory_transactions": { + "name": "inventory_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "inventory_item_id": { + "name": "inventory_item_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "inventory_transaction_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "quantity_delta": { + "name": "quantity_delta", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "unit": { + "name": "unit", + "type": "unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "ref_type": { + "name": "ref_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ref_id": { + "name": "ref_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "value_minor": { + "name": "value_minor", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "inventory_tx_item_idx": { + "name": "inventory_tx_item_idx", + "columns": [ + { + "expression": "inventory_item_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "inventory_tx_household_time_idx": { + "name": "inventory_tx_household_time_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "inventory_tx_type_idx": { + "name": "inventory_tx_type_idx", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "inventory_transactions_household_id_households_id_fk": { + "name": "inventory_transactions_household_id_households_id_fk", + "tableFrom": "inventory_transactions", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "inventory_transactions_inventory_item_id_inventory_items_id_fk": { + "name": "inventory_transactions_inventory_item_id_inventory_items_id_fk", + "tableFrom": "inventory_transactions", + "tableTo": "inventory_items", + "columnsFrom": [ + "inventory_item_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "inventory_transactions_actor_user_id_users_id_fk": { + "name": "inventory_transactions_actor_user_id_users_id_fk", + "tableFrom": "inventory_transactions", + "tableTo": "users", + "columnsFrom": [ + "actor_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.inventory_decay_profiles": { + "name": "inventory_decay_profiles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "half_life_days": { + "name": "half_life_days", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 7 + }, + "stale_after_days": { + "name": "stale_after_days", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 30 + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "inventory_decay_profiles_name_unique": { + "name": "inventory_decay_profiles_name_unique", + "nullsNotDistinct": false, + "columns": [ + "name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.inventory_item_decay_profile": { + "name": "inventory_item_decay_profile", + "schema": "", + "columns": { + "inventory_item_id": { + "name": "inventory_item_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "decay_profile_id": { + "name": "decay_profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "inventory_item_decay_profile_inventory_item_id_inventory_items_id_fk": { + "name": "inventory_item_decay_profile_inventory_item_id_inventory_items_id_fk", + "tableFrom": "inventory_item_decay_profile", + "tableTo": "inventory_items", + "columnsFrom": [ + "inventory_item_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "inventory_item_decay_profile_decay_profile_id_inventory_decay_profiles_id_fk": { + "name": "inventory_item_decay_profile_decay_profile_id_inventory_decay_profiles_id_fk", + "tableFrom": "inventory_item_decay_profile", + "tableTo": "inventory_decay_profiles", + "columnsFrom": [ + "decay_profile_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipe_cooks": { + "name": "recipe_cooks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "recipe_id": { + "name": "recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "portions_cooked": { + "name": "portions_cooked", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "cooked_at": { + "name": "cooked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "recipe_cooks_recipe_idx": { + "name": "recipe_cooks_recipe_idx", + "columns": [ + { + "expression": "recipe_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "recipe_cooks_user_idx": { + "name": "recipe_cooks_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "recipe_cooks_recipe_id_recipes_id_fk": { + "name": "recipe_cooks_recipe_id_recipes_id_fk", + "tableFrom": "recipe_cooks", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "recipe_cooks_user_id_users_id_fk": { + "name": "recipe_cooks_user_id_users_id_fk", + "tableFrom": "recipe_cooks", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipe_favorites": { + "name": "recipe_favorites", + "schema": "", + "columns": { + "recipe_id": { + "name": "recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "recipe_favorites_user_idx": { + "name": "recipe_favorites_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "recipe_favorites_recipe_id_recipes_id_fk": { + "name": "recipe_favorites_recipe_id_recipes_id_fk", + "tableFrom": "recipe_favorites", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "recipe_favorites_user_id_users_id_fk": { + "name": "recipe_favorites_user_id_users_id_fk", + "tableFrom": "recipe_favorites", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "recipe_favorites_recipe_id_user_id_pk": { + "name": "recipe_favorites_recipe_id_user_id_pk", + "columns": [ + "recipe_id", + "user_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipe_ingredients": { + "name": "recipe_ingredients", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "recipe_id": { + "name": "recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "canonical_ingredient_id": { + "name": "canonical_ingredient_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name_sv": { + "name": "display_name_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "unit": { + "name": "unit", + "type": "unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "optional": { + "name": "optional", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "recipe_ingredients_recipe_idx": { + "name": "recipe_ingredients_recipe_idx", + "columns": [ + { + "expression": "recipe_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "recipe_ingredients_canonical_idx": { + "name": "recipe_ingredients_canonical_idx", + "columns": [ + { + "expression": "canonical_ingredient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "recipe_ingredients_recipe_id_recipes_id_fk": { + "name": "recipe_ingredients_recipe_id_recipes_id_fk", + "tableFrom": "recipe_ingredients", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "recipe_ingredients_canonical_ingredient_id_canonical_ingredients_id_fk": { + "name": "recipe_ingredients_canonical_ingredient_id_canonical_ingredients_id_fk", + "tableFrom": "recipe_ingredients", + "tableTo": "canonical_ingredients", + "columnsFrom": [ + "canonical_ingredient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipe_ratings": { + "name": "recipe_ratings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "recipe_id": { + "name": "recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "stars": { + "name": "stars", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "feedback_tags": { + "name": "feedback_tags", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "comment": { + "name": "comment", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "recipe_ratings_user_recipe_unique": { + "name": "recipe_ratings_user_recipe_unique", + "columns": [ + { + "expression": "recipe_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "recipe_ratings_recipe_idx": { + "name": "recipe_ratings_recipe_idx", + "columns": [ + { + "expression": "recipe_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "recipe_ratings_recipe_id_recipes_id_fk": { + "name": "recipe_ratings_recipe_id_recipes_id_fk", + "tableFrom": "recipe_ratings", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "recipe_ratings_user_id_users_id_fk": { + "name": "recipe_ratings_user_id_users_id_fk", + "tableFrom": "recipe_ratings", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipe_similarities": { + "name": "recipe_similarities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "recipe_a_id": { + "name": "recipe_a_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recipe_b_id": { + "name": "recipe_b_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "similarity_score": { + "name": "similarity_score", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "classification": { + "name": "classification", + "type": "recipe_similarity_class", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "details": { + "name": "details", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "recipe_similarities_pair_unique": { + "name": "recipe_similarities_pair_unique", + "columns": [ + { + "expression": "recipe_a_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "recipe_b_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "recipe_similarities_recipe_a_id_recipes_id_fk": { + "name": "recipe_similarities_recipe_a_id_recipes_id_fk", + "tableFrom": "recipe_similarities", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_a_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "recipe_similarities_recipe_b_id_recipes_id_fk": { + "name": "recipe_similarities_recipe_b_id_recipes_id_fk", + "tableFrom": "recipe_similarities", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_b_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipe_source_registry": { + "name": "recipe_source_registry", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "source_name": { + "name": "source_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "license": { + "name": "license", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "right_to_store": { + "name": "right_to_store", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "right_to_modify": { + "name": "right_to_modify", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "right_to_display": { + "name": "right_to_display", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "attribution_required": { + "name": "attribution_required", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "attribution_text": { + "name": "attribution_text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "commercial_use": { + "name": "commercial_use", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "valid_from": { + "name": "valid_from", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "valid_to": { + "name": "valid_to", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipe_steps": { + "name": "recipe_steps", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "recipe_id": { + "name": "recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "step_number": { + "name": "step_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "instruction_sv": { + "name": "instruction_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timer_seconds": { + "name": "timer_seconds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "temperature_c": { + "name": "temperature_c", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "tip": { + "name": "tip", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "recipe_steps_recipe_idx": { + "name": "recipe_steps_recipe_idx", + "columns": [ + { + "expression": "recipe_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "recipe_steps_recipe_step_unique": { + "name": "recipe_steps_recipe_step_unique", + "columns": [ + { + "expression": "recipe_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "step_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "recipe_steps_recipe_id_recipes_id_fk": { + "name": "recipe_steps_recipe_id_recipes_id_fk", + "tableFrom": "recipe_steps", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipes": { + "name": "recipes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title_sv": { + "name": "title_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description_sv": { + "name": "description_sv", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "country": { + "name": "country", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cuisine": { + "name": "cuisine", + "type": "cuisine", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "meal_types": { + "name": "meal_types", + "type": "meal_type[]", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "tags": { + "name": "tags", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "methods": { + "name": "methods", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "equipment": { + "name": "equipment", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "difficulty": { + "name": "difficulty", + "type": "recipe_difficulty", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'easy'" + }, + "prep_time_minutes": { + "name": "prep_time_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "cook_time_minutes": { + "name": "cook_time_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_time_minutes": { + "name": "total_time_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "portions": { + "name": "portions", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 4 + }, + "nutrition_per_portion": { + "name": "nutrition_per_portion", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "allergens": { + "name": "allergens", + "type": "allergen[]", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "spice_level": { + "name": "spice_level", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "estimated_cost_minor_per_portion": { + "name": "estimated_cost_minor_per_portion", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "storage_guidance_sv": { + "name": "storage_guidance_sv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "meal_prep_friendly": { + "name": "meal_prep_friendly", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "freezer_friendly": { + "name": "freezer_friendly", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "peak_seasons": { + "name": "peak_seasons", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "holiday_tags": { + "name": "holiday_tags", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "dna": { + "name": "dna", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "variant_type": { + "name": "variant_type", + "type": "recipe_variant_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'standard'" + }, + "variant_of_recipe_id": { + "name": "variant_of_recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "forked_from_recipe_id": { + "name": "forked_from_recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "recipe_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "verification_status": { + "name": "verification_status", + "type": "recipe_verification_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'unverified'" + }, + "source_type": { + "name": "source_type", + "type": "recipe_source_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "source_registry_id": { + "name": "source_registry_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "creator_user_id": { + "name": "creator_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "creator_display_name": { + "name": "creator_display_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "image_urls": { + "name": "image_urls", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "rating_average": { + "name": "rating_average", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "rating_count": { + "name": "rating_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "cook_count": { + "name": "cook_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "favorite_count": { + "name": "favorite_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "moderation_note": { + "name": "moderation_note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "recipes_slug_unique": { + "name": "recipes_slug_unique", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "recipes_status_idx": { + "name": "recipes_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "recipes_cuisine_idx": { + "name": "recipes_cuisine_idx", + "columns": [ + { + "expression": "cuisine", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "recipes_variant_of_idx": { + "name": "recipes_variant_of_idx", + "columns": [ + { + "expression": "variant_of_recipe_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "recipes_creator_idx": { + "name": "recipes_creator_idx", + "columns": [ + { + "expression": "creator_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "recipes_total_time_idx": { + "name": "recipes_total_time_idx", + "columns": [ + { + "expression": "total_time_minutes", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "recipes_source_registry_id_recipe_source_registry_id_fk": { + "name": "recipes_source_registry_id_recipe_source_registry_id_fk", + "tableFrom": "recipes", + "tableTo": "recipe_source_registry", + "columnsFrom": [ + "source_registry_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "recipes_creator_user_id_users_id_fk": { + "name": "recipes_creator_user_id_users_id_fk", + "tableFrom": "recipes", + "tableTo": "users", + "columnsFrom": [ + "creator_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ingredient_translations": { + "name": "ingredient_translations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "ingredient_id": { + "name": "ingredient_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "language_tag": { + "name": "language_tag", + "type": "varchar(35)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "aliases": { + "name": "aliases", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "source": { + "name": "source", + "type": "translation_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'seed'" + }, + "status": { + "name": "status", + "type": "translation_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'published'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ingredient_translations_unique": { + "name": "ingredient_translations_unique", + "columns": [ + { + "expression": "ingredient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "language_tag", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ingredient_translations_lang_idx": { + "name": "ingredient_translations_lang_idx", + "columns": [ + { + "expression": "language_tag", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "ingredient_translations_ingredient_id_canonical_ingredients_id_fk": { + "name": "ingredient_translations_ingredient_id_canonical_ingredients_id_fk", + "tableFrom": "ingredient_translations", + "tableTo": "canonical_ingredients", + "columnsFrom": [ + "ingredient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipe_step_translations": { + "name": "recipe_step_translations", + "schema": "", + "columns": { + "recipe_id": { + "name": "recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "language_tag": { + "name": "language_tag", + "type": "varchar(35)", + "primaryKey": false, + "notNull": true + }, + "step_number": { + "name": "step_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "instruction": { + "name": "instruction", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tip": { + "name": "tip", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "recipe_step_translations_recipe_id_recipes_id_fk": { + "name": "recipe_step_translations_recipe_id_recipes_id_fk", + "tableFrom": "recipe_step_translations", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "recipe_step_translations_recipe_id_language_tag_step_number_pk": { + "name": "recipe_step_translations_recipe_id_language_tag_step_number_pk", + "columns": [ + "recipe_id", + "language_tag", + "step_number" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.recipe_translations": { + "name": "recipe_translations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "recipe_id": { + "name": "recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "language_tag": { + "name": "language_tag", + "type": "varchar(35)", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "storage_guidance": { + "name": "storage_guidance", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "translation_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'draft_ai'" + }, + "source": { + "name": "source", + "type": "translation_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'ai'" + }, + "verification": { + "name": "verification", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "recipe_translations_unique": { + "name": "recipe_translations_unique", + "columns": [ + { + "expression": "recipe_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "language_tag", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "recipe_translations_lang_idx": { + "name": "recipe_translations_lang_idx", + "columns": [ + { + "expression": "language_tag", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "recipe_translations_recipe_id_recipes_id_fk": { + "name": "recipe_translations_recipe_id_recipes_id_fk", + "tableFrom": "recipe_translations", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.unit_translations": { + "name": "unit_translations", + "schema": "", + "columns": { + "unit_code": { + "name": "unit_code", + "type": "unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "language_tag": { + "name": "language_tag", + "type": "varchar(35)", + "primaryKey": false, + "notNull": true + }, + "abbreviation": { + "name": "abbreviation", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "unit_translations_unit_code_language_tag_pk": { + "name": "unit_translations_unit_code_language_tag_pk", + "columns": [ + "unit_code", + "language_tag" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.allergen_market_rules": { + "name": "allergen_market_rules", + "schema": "", + "columns": { + "region_code": { + "name": "region_code", + "type": "varchar(2)", + "primaryKey": false, + "notNull": true + }, + "allergen": { + "name": "allergen", + "type": "allergen", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "must_highlight": { + "name": "must_highlight", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "allergen_market_rules_region_code_allergen_pk": { + "name": "allergen_market_rules_region_code_allergen_pk", + "columns": [ + "region_code", + "allergen" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.nutrition_display_profiles": { + "name": "nutrition_display_profiles", + "schema": "", + "columns": { + "region_code": { + "name": "region_code", + "type": "varchar(2)", + "primaryKey": true, + "notNull": true + }, + "energy_display": { + "name": "energy_display", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "salt_display": { + "name": "salt_display", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "energy_label_key": { + "name": "energy_label_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.meal_boxes": { + "name": "meal_boxes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "recipe_id": { + "name": "recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "title_sv": { + "name": "title_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "portions": { + "name": "portions", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "portions_remaining": { + "name": "portions_remaining", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "nutrition_per_portion": { + "name": "nutrition_per_portion", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "cooked_at": { + "name": "cooked_at", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "storage_location_id": { + "name": "storage_location_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "frozen": { + "name": "frozen", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "recommended_use_by": { + "name": "recommended_use_by", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "reserved_for_user_id": { + "name": "reserved_for_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'available'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "meal_boxes_household_idx": { + "name": "meal_boxes_household_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "meal_boxes_use_by_idx": { + "name": "meal_boxes_use_by_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "recommended_use_by", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "meal_boxes_household_id_households_id_fk": { + "name": "meal_boxes_household_id_households_id_fk", + "tableFrom": "meal_boxes", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "meal_boxes_recipe_id_recipes_id_fk": { + "name": "meal_boxes_recipe_id_recipes_id_fk", + "tableFrom": "meal_boxes", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "meal_boxes_storage_location_id_storage_locations_id_fk": { + "name": "meal_boxes_storage_location_id_storage_locations_id_fk", + "tableFrom": "meal_boxes", + "tableTo": "storage_locations", + "columnsFrom": [ + "storage_location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "meal_boxes_reserved_for_user_id_users_id_fk": { + "name": "meal_boxes_reserved_for_user_id_users_id_fk", + "tableFrom": "meal_boxes", + "tableTo": "users", + "columnsFrom": [ + "reserved_for_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.meals": { + "name": "meals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "meal_type": { + "name": "meal_type", + "type": "meal_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "meal_log_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "recipe_id": { + "name": "recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "title_sv": { + "name": "title_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "portion_fraction": { + "name": "portion_fraction", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "nutrition": { + "name": "nutrition", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "nutrition_is_estimate": { + "name": "nutrition_is_estimate", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "estimate_min_kcal": { + "name": "estimate_min_kcal", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "estimate_max_kcal": { + "name": "estimate_max_kcal", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "items": { + "name": "items", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "photo_url": { + "name": "photo_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scan_job_id": { + "name": "scan_job_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "logged_at": { + "name": "logged_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "meals_user_date_idx": { + "name": "meals_user_date_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "meals_household_idx": { + "name": "meals_household_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "meals_user_id_users_id_fk": { + "name": "meals_user_id_users_id_fk", + "tableFrom": "meals", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "meals_household_id_households_id_fk": { + "name": "meals_household_id_households_id_fk", + "tableFrom": "meals", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "meals_recipe_id_recipes_id_fk": { + "name": "meals_recipe_id_recipes_id_fk", + "tableFrom": "meals", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.shopping_list_items": { + "name": "shopping_list_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "shopping_list_id": { + "name": "shopping_list_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "canonical_ingredient_id": { + "name": "canonical_ingredient_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "unit": { + "name": "unit", + "type": "unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'COUNT'" + }, + "store_section": { + "name": "store_section", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'hygien_ovrigt'" + }, + "suggested_package_size": { + "name": "suggested_package_size", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "estimated_price_minor": { + "name": "estimated_price_minor", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "checked": { + "name": "checked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "added_by_user_id": { + "name": "added_by_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "origin": { + "name": "origin", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "shopping_list_items_list_idx": { + "name": "shopping_list_items_list_idx", + "columns": [ + { + "expression": "shopping_list_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "shopping_list_items_shopping_list_id_shopping_lists_id_fk": { + "name": "shopping_list_items_shopping_list_id_shopping_lists_id_fk", + "tableFrom": "shopping_list_items", + "tableTo": "shopping_lists", + "columnsFrom": [ + "shopping_list_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "shopping_list_items_canonical_ingredient_id_canonical_ingredients_id_fk": { + "name": "shopping_list_items_canonical_ingredient_id_canonical_ingredients_id_fk", + "tableFrom": "shopping_list_items", + "tableTo": "canonical_ingredients", + "columnsFrom": [ + "canonical_ingredient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "shopping_list_items_added_by_user_id_users_id_fk": { + "name": "shopping_list_items_added_by_user_id_users_id_fk", + "tableFrom": "shopping_list_items", + "tableTo": "users", + "columnsFrom": [ + "added_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.shopping_lists": { + "name": "shopping_lists", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'Inköpslista'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "week_plan_id": { + "name": "week_plan_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "shopping_lists_household_idx": { + "name": "shopping_lists_household_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "shopping_lists_household_id_households_id_fk": { + "name": "shopping_lists_household_id_households_id_fk", + "tableFrom": "shopping_lists", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "shopping_lists_week_plan_id_week_plans_id_fk": { + "name": "shopping_lists_week_plan_id_week_plans_id_fk", + "tableFrom": "shopping_lists", + "tableTo": "week_plans", + "columnsFrom": [ + "week_plan_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.week_plan_entries": { + "name": "week_plan_entries", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "week_plan_id": { + "name": "week_plan_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "meal_type": { + "name": "meal_type", + "type": "meal_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "recipe_id": { + "name": "recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "meal_box_id": { + "name": "meal_box_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "title_sv": { + "name": "title_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "portions": { + "name": "portions", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 2 + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'planned'" + }, + "reschedule_reason_sv": { + "name": "reschedule_reason_sv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "week_plan_entries_plan_idx": { + "name": "week_plan_entries_plan_idx", + "columns": [ + { + "expression": "week_plan_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "week_plan_entries_week_plan_id_week_plans_id_fk": { + "name": "week_plan_entries_week_plan_id_week_plans_id_fk", + "tableFrom": "week_plan_entries", + "tableTo": "week_plans", + "columnsFrom": [ + "week_plan_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "week_plan_entries_recipe_id_recipes_id_fk": { + "name": "week_plan_entries_recipe_id_recipes_id_fk", + "tableFrom": "week_plan_entries", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "week_plan_entries_meal_box_id_meal_boxes_id_fk": { + "name": "week_plan_entries_meal_box_id_meal_boxes_id_fk", + "tableFrom": "week_plan_entries", + "tableTo": "meal_boxes", + "columnsFrom": [ + "meal_box_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.week_plans": { + "name": "week_plans", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "week_start_date": { + "name": "week_start_date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "generated_by": { + "name": "generated_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "week_plans_household_week_idx": { + "name": "week_plans_household_week_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "week_start_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "week_plans_household_id_households_id_fk": { + "name": "week_plans_household_id_households_id_fk", + "tableFrom": "week_plans", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.price_observations": { + "name": "price_observations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "canonical_ingredient_id": { + "name": "canonical_ingredient_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "store_name": { + "name": "store_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "price_minor": { + "name": "price_minor", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "unit": { + "name": "unit", + "type": "unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "price_per_kg_minor": { + "name": "price_per_kg_minor", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "observed_at": { + "name": "observed_at", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'receipt'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "price_observations_ingredient_idx": { + "name": "price_observations_ingredient_idx", + "columns": [ + { + "expression": "canonical_ingredient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "observed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "price_observations_canonical_ingredient_id_canonical_ingredients_id_fk": { + "name": "price_observations_canonical_ingredient_id_canonical_ingredients_id_fk", + "tableFrom": "price_observations", + "tableTo": "canonical_ingredients", + "columnsFrom": [ + "canonical_ingredient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "price_observations_product_id_products_id_fk": { + "name": "price_observations_product_id_products_id_fk", + "tableFrom": "price_observations", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.receipt_lines": { + "name": "receipt_lines", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "receipt_id": { + "name": "receipt_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "raw_text": { + "name": "raw_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "normalized_name": { + "name": "normalized_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "canonical_ingredient_id": { + "name": "canonical_ingredient_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "product_id": { + "name": "product_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "quantity": { + "name": "quantity", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "unit": { + "name": "unit", + "type": "unit", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "unit_price_minor": { + "name": "unit_price_minor", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_price_minor": { + "name": "total_price_minor", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "confidence": { + "name": "confidence", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "verified_by_user": { + "name": "verified_by_user", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "added_to_inventory": { + "name": "added_to_inventory", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "receipt_lines_receipt_idx": { + "name": "receipt_lines_receipt_idx", + "columns": [ + { + "expression": "receipt_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "receipt_lines_receipt_id_receipts_id_fk": { + "name": "receipt_lines_receipt_id_receipts_id_fk", + "tableFrom": "receipt_lines", + "tableTo": "receipts", + "columnsFrom": [ + "receipt_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "receipt_lines_canonical_ingredient_id_canonical_ingredients_id_fk": { + "name": "receipt_lines_canonical_ingredient_id_canonical_ingredients_id_fk", + "tableFrom": "receipt_lines", + "tableTo": "canonical_ingredients", + "columnsFrom": [ + "canonical_ingredient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "receipt_lines_product_id_products_id_fk": { + "name": "receipt_lines_product_id_products_id_fk", + "tableFrom": "receipt_lines", + "tableTo": "products", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.receipts": { + "name": "receipts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "store_name": { + "name": "store_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "purchase_date": { + "name": "purchase_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "total_minor": { + "name": "total_minor", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "discount_minor": { + "name": "discount_minor", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "image_url": { + "name": "image_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scan_job_id": { + "name": "scan_job_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "receipts_household_idx": { + "name": "receipts_household_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "purchase_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "receipts_household_id_households_id_fk": { + "name": "receipts_household_id_households_id_fk", + "tableFrom": "receipts", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_corrections": { + "name": "ai_corrections", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "scan_job_id": { + "name": "scan_job_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "task_type": { + "name": "task_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ai_output": { + "name": "ai_output", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "user_correction": { + "name": "user_correction", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "model_version": { + "name": "model_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prompt_version": { + "name": "prompt_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "consent_snapshot": { + "name": "consent_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "exported_to_training": { + "name": "exported_to_training", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "training_batch_id": { + "name": "training_batch_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ai_corrections_task_idx": { + "name": "ai_corrections_task_idx", + "columns": [ + { + "expression": "task_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "ai_corrections_scan_job_id_scan_jobs_id_fk": { + "name": "ai_corrections_scan_job_id_scan_jobs_id_fk", + "tableFrom": "ai_corrections", + "tableTo": "scan_jobs", + "columnsFrom": [ + "scan_job_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "ai_corrections_user_id_users_id_fk": { + "name": "ai_corrections_user_id_users_id_fk", + "tableFrom": "ai_corrections", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scan_jobs": { + "name": "scan_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "scan_type": { + "name": "scan_type", + "type": "scan_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "job_type": { + "name": "job_type", + "type": "job_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "job_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'queued'" + }, + "s3_keys": { + "name": "s3_keys", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "context": { + "name": "context", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model_version": { + "name": "model_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prompt_version": { + "name": "prompt_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "latency_ms": { + "name": "latency_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "cost_usd": { + "name": "cost_usd", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scan_jobs_user_idx": { + "name": "scan_jobs_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "scan_jobs_status_idx": { + "name": "scan_jobs_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "scan_jobs_user_id_users_id_fk": { + "name": "scan_jobs_user_id_users_id_fk", + "tableFrom": "scan_jobs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "scan_jobs_household_id_households_id_fk": { + "name": "scan_jobs_household_id_households_id_fk", + "tableFrom": "scan_jobs", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.food_memories": { + "name": "food_memories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "recipe_id": { + "name": "recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "title_sv": { + "name": "title_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "summary_sv": { + "name": "summary_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "holiday_tag": { + "name": "holiday_tag", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "photo_url": { + "name": "photo_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stars": { + "name": "stars", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "food_memories_household_idx": { + "name": "food_memories_household_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "food_memories_holiday_idx": { + "name": "food_memories_holiday_idx", + "columns": [ + { + "expression": "holiday_tag", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "food_memories_user_id_users_id_fk": { + "name": "food_memories_user_id_users_id_fk", + "tableFrom": "food_memories", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "food_memories_household_id_households_id_fk": { + "name": "food_memories_household_id_households_id_fk", + "tableFrom": "food_memories", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "food_memories_recipe_id_recipes_id_fk": { + "name": "food_memories_recipe_id_recipes_id_fk", + "tableFrom": "food_memories", + "tableTo": "recipes", + "columnsFrom": [ + "recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.memory_items": { + "name": "memory_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "kind": { + "name": "kind", + "type": "memory_kind", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "summary_sv": { + "name": "summary_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "origin": { + "name": "origin", + "type": "signal_origin", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "confidence": { + "name": "confidence", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0.5 + }, + "verified_by_user": { + "name": "verified_by_user", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "paused": { + "name": "paused", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "memory_items_user_idx": { + "name": "memory_items_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "memory_items_household_idx": { + "name": "memory_items_household_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "memory_items_key_idx": { + "name": "memory_items_key_idx", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "memory_items_user_id_users_id_fk": { + "name": "memory_items_user_id_users_id_fk", + "tableFrom": "memory_items", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "memory_items_household_id_households_id_fk": { + "name": "memory_items_household_id_households_id_fk", + "tableFrom": "memory_items", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.taste_signals": { + "name": "taste_signals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "axis": { + "name": "axis", + "type": "taste_axis", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "direction": { + "name": "direction", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "strength": { + "name": "strength", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0.5 + }, + "origin": { + "name": "origin", + "type": "signal_origin", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "ref_recipe_id": { + "name": "ref_recipe_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "taste_signals_user_idx": { + "name": "taste_signals_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "axis", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "taste_signals_user_id_users_id_fk": { + "name": "taste_signals_user_id_users_id_fk", + "tableFrom": "taste_signals", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "taste_signals_ref_recipe_id_recipes_id_fk": { + "name": "taste_signals_ref_recipe_id_recipes_id_fk", + "tableFrom": "taste_signals", + "tableTo": "recipes", + "columnsFrom": [ + "ref_recipe_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_events": { + "name": "season_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name_sv": { + "name": "name_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "market": { + "name": "market", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'SE'" + }, + "date_rule": { + "name": "date_rule", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "lead_days": { + "name": "lead_days", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 7 + }, + "food_tags": { + "name": "food_tags", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "recipe_slugs": { + "name": "recipe_slugs", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "season_events_slug_market_unique": { + "name": "season_events_slug_market_unique", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "market", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_usage_counters": { + "name": "ai_usage_counters", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "month": { + "name": "month", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ai_scans": { + "name": "ai_scans", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "ai_tokens_in": { + "name": "ai_tokens_in", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "ai_tokens_out": { + "name": "ai_tokens_out", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ai_usage_user_month_unique": { + "name": "ai_usage_user_month_unique", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "month", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "ai_usage_counters_user_id_users_id_fk": { + "name": "ai_usage_counters_user_id_users_id_fk", + "tableFrom": "ai_usage_counters", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.store_notifications": { + "name": "store_notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "subscription_provider", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "notification_type": { + "name": "notification_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "raw_payload": { + "name": "raw_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "signature_verified": { + "name": "signature_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "processed": { + "name": "processed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "store_notifications_pending_idx": { + "name": "store_notifications_pending_idx", + "columns": [ + { + "expression": "processed", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.store_transactions": { + "name": "store_transactions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "subscription_provider", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "transaction_id": { + "name": "transaction_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "original_transaction_id": { + "name": "original_transaction_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "product_id": { + "name": "product_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "raw_payload": { + "name": "raw_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "store_transactions_unique": { + "name": "store_transactions_unique", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "transaction_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "store_transactions_user_id_users_id_fk": { + "name": "store_transactions_user_id_users_id_fk", + "tableFrom": "store_transactions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.subscription_events": { + "name": "subscription_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "subscription_id": { + "name": "subscription_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "subscription_events_sub_idx": { + "name": "subscription_events_sub_idx", + "columns": [ + { + "expression": "subscription_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "subscription_events_subscription_id_subscriptions_id_fk": { + "name": "subscription_events_subscription_id_subscriptions_id_fk", + "tableFrom": "subscription_events", + "tableTo": "subscriptions", + "columnsFrom": [ + "subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "subscription_events_user_id_users_id_fk": { + "name": "subscription_events_user_id_users_id_fk", + "tableFrom": "subscription_events", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.subscriptions": { + "name": "subscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "provider": { + "name": "provider", + "type": "subscription_provider", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "product_id": { + "name": "product_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "plan": { + "name": "plan", + "type": "subscription_plan", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "original_transaction_id": { + "name": "original_transaction_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "subscription_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "purchased_at": { + "name": "purchased_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "grace_period_expires_at": { + "name": "grace_period_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "canceled_at": { + "name": "canceled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_verified_at": { + "name": "last_verified_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "subscriptions_user_idx": { + "name": "subscriptions_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "subscriptions_original_tx_unique": { + "name": "subscriptions_original_tx_unique", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "original_transaction_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"subscriptions\".\"original_transaction_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "subscriptions_user_id_users_id_fk": { + "name": "subscriptions_user_id_users_id_fk", + "tableFrom": "subscriptions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "subscriptions_household_id_households_id_fk": { + "name": "subscriptions_household_id_households_id_fk", + "tableFrom": "subscriptions", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.trials": { + "name": "trials", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ends_at": { + "name": "ends_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "trials_user_id_users_id_fk": { + "name": "trials_user_id_users_id_fk", + "tableFrom": "trials", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.analytics_daily_snapshots": { + "name": "analytics_daily_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "date": { + "name": "date", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "event_name": { + "name": "event_name", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "count": { + "name": "count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "unique_sessions": { + "name": "unique_sessions", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "properties_fingerprint": { + "name": "properties_fingerprint", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "pa_daily_household_date_idx": { + "name": "pa_daily_household_date_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pa_daily_date_event_idx": { + "name": "pa_daily_date_event_idx", + "columns": [ + { + "expression": "date", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "event_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "analytics_daily_snapshots_household_id_households_id_fk": { + "name": "analytics_daily_snapshots_household_id_households_id_fk", + "tableFrom": "analytics_daily_snapshots", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.product_analytics_events": { + "name": "product_analytics_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "received_at": { + "name": "received_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "event_name": { + "name": "event_name", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "anonymous_id": { + "name": "anonymous_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "session_id": { + "name": "session_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "app_version": { + "name": "app_version", + "type": "varchar(32)", + "primaryKey": false, + "notNull": false + }, + "platform": { + "name": "platform", + "type": "varchar(16)", + "primaryKey": false, + "notNull": false + }, + "locale": { + "name": "locale", + "type": "varchar(16)", + "primaryKey": false, + "notNull": false + }, + "experiment_variant": { + "name": "experiment_variant", + "type": "varchar(128)", + "primaryKey": false, + "notNull": false + }, + "properties": { + "name": "properties", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + } + }, + "indexes": { + "pa_events_name_occurred_idx": { + "name": "pa_events_name_occurred_idx", + "columns": [ + { + "expression": "event_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pa_events_household_occurred_idx": { + "name": "pa_events_household_occurred_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pa_events_user_occurred_idx": { + "name": "pa_events_user_occurred_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pa_events_session_idx": { + "name": "pa_events_session_idx", + "columns": [ + { + "expression": "session_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pa_events_received_idx": { + "name": "pa_events_received_idx", + "columns": [ + { + "expression": "received_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "product_analytics_events_user_id_users_id_fk": { + "name": "product_analytics_events_user_id_users_id_fk", + "tableFrom": "product_analytics_events", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "product_analytics_events_household_id_households_id_fk": { + "name": "product_analytics_events_household_id_households_id_fk", + "tableFrom": "product_analytics_events", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_eval_cases": { + "name": "ai_eval_cases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "task_type": { + "name": "task_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name_sv": { + "name": "name_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "input_ref": { + "name": "input_ref", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "expected_output": { + "name": "expected_output", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'standard'" + }, + "active": { + "name": "active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_eval_runs": { + "name": "ai_eval_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "task_type": { + "name": "task_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model_version": { + "name": "model_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prompt_version": { + "name": "prompt_version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "metrics": { + "name": "metrics", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "passed": { + "name": "passed", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ai_eval_runs_task_idx": { + "name": "ai_eval_runs_task_idx", + "columns": [ + { + "expression": "task_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.audit_logs": { + "name": "audit_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "actor_type": { + "name": "actor_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_type": { + "name": "target_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_id": { + "name": "target_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "ip": { + "name": "ip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "correlation_id": { + "name": "correlation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "audit_logs_actor_idx": { + "name": "audit_logs_actor_idx", + "columns": [ + { + "expression": "actor_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "audit_logs_action_idx": { + "name": "audit_logs_action_idx", + "columns": [ + { + "expression": "action", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.creator_follows": { + "name": "creator_follows", + "schema": "", + "columns": { + "follower_user_id": { + "name": "follower_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "creator_user_id": { + "name": "creator_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "creator_follows_follower_user_id_users_id_fk": { + "name": "creator_follows_follower_user_id_users_id_fk", + "tableFrom": "creator_follows", + "tableTo": "users", + "columnsFrom": [ + "follower_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "creator_follows_creator_user_id_users_id_fk": { + "name": "creator_follows_creator_user_id_users_id_fk", + "tableFrom": "creator_follows", + "tableTo": "users", + "columnsFrom": [ + "creator_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "creator_follows_follower_user_id_creator_user_id_pk": { + "name": "creator_follows_follower_user_id_creator_user_id_pk", + "columns": [ + "follower_user_id", + "creator_user_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.creator_stats": { + "name": "creator_stats", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "visibility": { + "name": "visibility", + "type": "profile_visibility", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'private'" + }, + "level": { + "name": "level", + "type": "creator_level", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'beginner'" + }, + "published_recipes": { + "name": "published_recipes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "followers": { + "name": "followers", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_cooks": { + "name": "total_cooks", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_favorites": { + "name": "total_favorites", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "average_rating": { + "name": "average_rating", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "verified_recipes": { + "name": "verified_recipes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "badges": { + "name": "badges", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "creator_stats_user_id_users_id_fk": { + "name": "creator_stats_user_id_users_id_fk", + "tableFrom": "creator_stats", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.domain_events": { + "name": "domain_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "type": { + "name": "type", + "type": "event_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "correlation_id": { + "name": "correlation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "published_at": { + "name": "published_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "domain_events_unpublished_idx": { + "name": "domain_events_unpublished_idx", + "columns": [ + { + "expression": "published_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "domain_events_type_idx": { + "name": "domain_events_type_idx", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "domain_events_household_idx": { + "name": "domain_events_household_idx", + "columns": [ + { + "expression": "household_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "occurred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.feature_flags": { + "name": "feature_flags", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "description_sv": { + "name": "description_sv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rollout_percent": { + "name": "rollout_percent", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 100 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.idempotency_keys": { + "name": "idempotency_keys", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "response_status": { + "name": "response_status", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "response_body": { + "name": "response_body", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "idempotency_keys_user_id_users_id_fk": { + "name": "idempotency_keys_user_id_users_id_fk", + "tableFrom": "idempotency_keys", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "idempotency_keys_user_id_key_endpoint_pk": { + "name": "idempotency_keys_user_id_key_endpoint_pk", + "columns": [ + "user_id", + "key", + "endpoint" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notifications": { + "name": "notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "notification_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "title_sv": { + "name": "title_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body_sv": { + "name": "body_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "template_key": { + "name": "template_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "variables": { + "name": "variables", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "locale": { + "name": "locale", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scheduled_for": { + "name": "scheduled_for", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "sent_at": { + "name": "sent_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "read_at": { + "name": "read_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "notifications_user_idx": { + "name": "notifications_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "notifications_user_id_users_id_fk": { + "name": "notifications_user_id_users_id_fk", + "tableFrom": "notifications", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.push_tokens": { + "name": "push_tokens", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "push_tokens_user_id_users_id_fk": { + "name": "push_tokens_user_id_users_id_fk", + "tableFrom": "push_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "push_tokens_user_id_token_pk": { + "name": "push_tokens_user_id_token_pk", + "columns": [ + "user_id", + "token" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.waste_summaries": { + "name": "waste_summaries", + "schema": "", + "columns": { + "household_id": { + "name": "household_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "week_start_date": { + "name": "week_start_date", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "discarded_count": { + "name": "discarded_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "discarded_value_minor": { + "name": "discarded_value_minor", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "waste_summaries_household_id_households_id_fk": { + "name": "waste_summaries_household_id_households_id_fk", + "tableFrom": "waste_summaries", + "tableTo": "households", + "columnsFrom": [ + "household_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "waste_summaries_household_id_week_start_date_pk": { + "name": "waste_summaries_household_id_week_start_date_pk", + "columns": [ + "household_id", + "week_start_date" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.release_gates": { + "name": "release_gates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "gate_key": { + "name": "gate_key", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "release_gate_category", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "name_sv": { + "name": "name_sv", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name_en": { + "name": "name_en", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description_sv": { + "name": "description_sv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description_en": { + "name": "description_en", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_value": { + "name": "target_value", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "comparison": { + "name": "comparison", + "type": "release_gate_comparison", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "evaluation_window_days": { + "name": "evaluation_window_days", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 7 + }, + "last_value": { + "name": "last_value", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "last_evaluated_at": { + "name": "last_evaluated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "release_gate_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "blocking": { + "name": "blocking", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "release_gates_category_idx": { + "name": "release_gates_category_idx", + "columns": [ + { + "expression": "category", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "release_gates_status_idx": { + "name": "release_gates_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "release_gates_gate_key_unique": { + "name": "release_gates_gate_key_unique", + "nullsNotDistinct": false, + "columns": [ + "gate_key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.activity_level": { + "name": "activity_level", + "schema": "public", + "values": [ + "sedentary", + "light", + "moderate", + "active", + "very_active" + ] + }, + "public.allergen": { + "name": "allergen", + "schema": "public", + "values": [ + "gluten", + "crustaceans", + "eggs", + "fish", + "peanuts", + "soy", + "milk", + "tree_nuts", + "celery", + "mustard", + "sesame", + "sulphites", + "lupin", + "molluscs" + ] + }, + "public.consent_kind": { + "name": "consent_kind", + "schema": "public", + "values": [ + "personalization", + "anonymized_improvement", + "image_training", + "health_integration", + "location_weather", + "push_notifications", + "product_analytics" + ] + }, + "public.consent_status": { + "name": "consent_status", + "schema": "public", + "values": [ + "granted", + "denied", + "revoked" + ] + }, + "public.cooking_method": { + "name": "cooking_method", + "schema": "public", + "values": [ + "stovetop", + "oven", + "grill", + "airfryer", + "wok", + "slow_cooker", + "sous_vide", + "microwave", + "no_cook", + "pressure_cooker", + "deep_fry", + "steam" + ] + }, + "public.creator_level": { + "name": "creator_level", + "schema": "public", + "values": [ + "beginner", + "sous_chef", + "chef", + "master_chef", + "legend" + ] + }, + "public.cuisine": { + "name": "cuisine", + "schema": "public", + "values": [ + "swedish", + "nordic", + "italian", + "french", + "spanish", + "greek", + "thai", + "chinese", + "japanese", + "korean", + "vietnamese", + "indian", + "mexican", + "american", + "turkish", + "lebanese", + "moroccan", + "middle_eastern", + "international" + ] + }, + "public.date_kind": { + "name": "date_kind", + "schema": "public", + "values": [ + "best_before", + "use_by" + ] + }, + "public.diet_pattern": { + "name": "diet_pattern", + "schema": "public", + "values": [ + "omnivore", + "flexitarian", + "pescatarian", + "vegetarian", + "vegan", + "low_carb", + "keto", + "high_protein", + "mediterranean" + ] + }, + "public.event_type": { + "name": "event_type", + "schema": "public", + "values": [ + "PRODUCT_ADDED", + "PRODUCT_UPDATED", + "PRODUCT_CONSUMED", + "PRODUCT_DISCARDED", + "RECIPE_COOKED", + "RECIPE_RATED", + "RECIPE_CREATED", + "RECIPE_FORKED", + "MEAL_LOGGED", + "MEAL_PHOTO_ANALYZED", + "HOUSEHOLD_MEMBER_ADDED", + "SUBSCRIPTION_STARTED", + "SUBSCRIPTION_CHANGED", + "AI_CORRECTED", + "MEMORY_UPDATED", + "SHOPPING_COMPLETED", + "WEEK_PLAN_UPDATED", + "MEAL_BOX_CREATED", + "MEAL_BOX_CONSUMED" + ] + }, + "public.expiry_status": { + "name": "expiry_status", + "schema": "public", + "values": [ + "fresh", + "use_soon", + "expiring", + "expired", + "unknown" + ] + }, + "public.goal_type": { + "name": "goal_type", + "schema": "public", + "values": [ + "lose_weight", + "gain_weight", + "build_muscle", + "maintain_weight", + "more_protein", + "less_fat", + "more_fiber", + "more_variety", + "less_waste", + "lower_cost", + "cook_more" + ] + }, + "public.household_role": { + "name": "household_role", + "schema": "public", + "values": [ + "owner", + "adult", + "member", + "child" + ] + }, + "public.inventory_source": { + "name": "inventory_source", + "schema": "public", + "values": [ + "fridge_photo", + "freezer_photo", + "pantry_photo", + "ingredient_photo", + "barcode", + "receipt", + "digital_receipt", + "label_photo", + "manual_search", + "free_text", + "voice", + "cooked_recipe", + "connector", + "seed" + ] + }, + "public.inventory_transaction_type": { + "name": "inventory_transaction_type", + "schema": "public", + "values": [ + "purchase", + "consume", + "discard", + "adjust", + "cook_use", + "leftover_created", + "leftover_consumed", + "correction", + "freeze", + "thaw", + "move" + ] + }, + "public.job_status": { + "name": "job_status", + "schema": "public", + "values": [ + "queued", + "running", + "awaiting_confirmation", + "completed", + "failed", + "canceled" + ] + }, + "public.job_type": { + "name": "job_type", + "schema": "public", + "values": [ + "ANALYZE_FRIDGE_IMAGE", + "ANALYZE_PANTRY_IMAGE", + "ANALYZE_MEAL_IMAGE", + "READ_RECEIPT", + "READ_NUTRITION_LABEL", + "READ_EXPIRY_DATE", + "NORMALIZE_PRODUCTS", + "DEDUPLICATE_INVENTORY", + "CALCULATE_NUTRITION", + "GENERATE_RECIPE_OPTIONS", + "RANK_RECIPES", + "UPDATE_USER_MEMORY", + "GENERATE_WEEK_PLAN", + "SEND_EXPIRY_NOTIFICATION", + "VERIFY_SUBSCRIPTION", + "PROCESS_STORE_NOTIFICATION", + "BUILD_TRAINING_SAMPLE", + "RUN_AI_EVALUATION" + ] + }, + "public.meal_log_source": { + "name": "meal_log_source", + "schema": "public", + "values": [ + "cooked_recipe", + "plate_photo", + "barcode", + "product", + "free_text", + "voice", + "previous_meal", + "meal_box", + "manual" + ] + }, + "public.meal_type": { + "name": "meal_type", + "schema": "public", + "values": [ + "breakfast", + "lunch", + "dinner", + "snack", + "dessert", + "starter", + "buffet", + "party" + ] + }, + "public.memory_kind": { + "name": "memory_kind", + "schema": "public", + "values": [ + "structured_fact", + "event", + "semantic", + "profile_summary", + "recipe_memory" + ] + }, + "public.notification_type": { + "name": "notification_type", + "schema": "public", + "values": [ + "expiry_warning", + "meal_box_reminder", + "quick_dinner_suggestion", + "holiday_upcoming", + "creator_new_recipe", + "week_plan_change", + "pantry_forecast", + "subscription_status" + ] + }, + "public.precision_mode": { + "name": "precision_mode", + "schema": "public", + "values": [ + "simple", + "exact" + ] + }, + "public.profile_visibility": { + "name": "profile_visibility", + "schema": "public", + "values": [ + "private", + "friends", + "national", + "global" + ] + }, + "public.recipe_difficulty": { + "name": "recipe_difficulty", + "schema": "public", + "values": [ + "beginner", + "easy", + "medium", + "advanced", + "expert" + ] + }, + "public.recipe_similarity_class": { + "name": "recipe_similarity_class", + "schema": "public", + "values": [ + "duplicate", + "variant", + "inspired", + "independent" + ] + }, + "public.recipe_source_type": { + "name": "recipe_source_type", + "schema": "public", + "values": [ + "own_editorial", + "ai_assisted_reviewed", + "open_license", + "public_domain", + "licensed_database", + "creator_agreement", + "user_generated" + ] + }, + "public.recipe_status": { + "name": "recipe_status", + "schema": "public", + "values": [ + "draft", + "submitted", + "ai_checked", + "in_moderation", + "published", + "rejected", + "archived" + ] + }, + "public.recipe_variant_type": { + "name": "recipe_variant_type", + "schema": "public", + "values": [ + "standard", + "high_protein", + "low_calorie", + "low_fat", + "vegetarian", + "vegan", + "gluten_free", + "lactose_free", + "kid_friendly", + "airfryer", + "budget" + ] + }, + "public.recipe_verification_status": { + "name": "recipe_verification_status", + "schema": "public", + "values": [ + "unverified", + "community", + "verified", + "editorial" + ] + }, + "public.religious_rule": { + "name": "religious_rule", + "schema": "public", + "values": [ + "none", + "halal", + "kosher", + "hindu_no_beef", + "buddhist_vegetarian" + ] + }, + "public.scan_type": { + "name": "scan_type", + "schema": "public", + "values": [ + "fridge", + "freezer", + "pantry", + "ingredients", + "plate", + "receipt", + "barcode", + "expiry_date", + "nutrition_label", + "product_package" + ] + }, + "public.sex": { + "name": "sex", + "schema": "public", + "values": [ + "female", + "male", + "unspecified" + ] + }, + "public.signal_origin": { + "name": "signal_origin", + "schema": "public", + "values": [ + "user_stated", + "observed", + "ai_inferred" + ] + }, + "public.storage_location_type": { + "name": "storage_location_type", + "schema": "public", + "values": [ + "fridge", + "freezer", + "pantry", + "garage_freezer", + "wine_fridge", + "cellar", + "meal_box", + "custom" + ] + }, + "public.subscription_plan": { + "name": "subscription_plan", + "schema": "public", + "values": [ + "free", + "household", + "family", + "large_household" + ] + }, + "public.subscription_provider": { + "name": "subscription_provider", + "schema": "public", + "values": [ + "apple", + "google", + "promo", + "none" + ] + }, + "public.subscription_status": { + "name": "subscription_status", + "schema": "public", + "values": [ + "trial", + "active", + "in_grace", + "on_hold", + "paused", + "canceled", + "expired" + ] + }, + "public.taste_axis": { + "name": "taste_axis", + "schema": "public", + "values": [ + "spice", + "salt", + "acid", + "creaminess", + "garlic", + "sweetness", + "herbs", + "umami" + ] + }, + "public.unit": { + "name": "unit", + "schema": "public", + "values": [ + "GRAM", + "KILOGRAM", + "MILLILITER", + "DECILITER", + "LITER", + "TEASPOON", + "TABLESPOON", + "CUP_US", + "FLUID_OUNCE_US", + "OUNCE", + "POUND", + "COUNT", + "PORTION", + "PINCH", + "SLICE", + "CLOVE", + "CAN", + "PACKAGE" + ] + }, + "public.user_role": { + "name": "user_role", + "schema": "public", + "values": [ + "user", + "moderator", + "admin" + ] + }, + "public.verification_status": { + "name": "verification_status", + "schema": "public", + "values": [ + "unverified", + "user_verified", + "editorially_verified" + ] + }, + "public.measurement_system": { + "name": "measurement_system", + "schema": "public", + "values": [ + "METRIC", + "US_CUSTOMARY", + "MIXED" + ] + }, + "public.temperature_unit": { + "name": "temperature_unit", + "schema": "public", + "values": [ + "CELSIUS", + "FAHRENHEIT" + ] + }, + "public.translation_source": { + "name": "translation_source", + "schema": "public", + "values": [ + "seed", + "ai", + "human" + ] + }, + "public.translation_status": { + "name": "translation_status", + "schema": "public", + "values": [ + "draft_ai", + "in_review", + "published" + ] + }, + "public.release_gate_category": { + "name": "release_gate_category", + "schema": "public", + "values": [ + "product", + "quality", + "retention", + "economy" + ] + }, + "public.release_gate_comparison": { + "name": "release_gate_comparison", + "schema": "public", + "values": [ + "gte", + "lte", + "eq" + ] + }, + "public.release_gate_status": { + "name": "release_gate_status", + "schema": "public", + "values": [ + "pending", + "passed", + "failed", + "blocked", + "not_measurable" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/infrastructure/migrations/meta/_journal.json b/infrastructure/migrations/meta/_journal.json index 6414182..73e4bfe 100644 --- a/infrastructure/migrations/meta/_journal.json +++ b/infrastructure/migrations/meta/_journal.json @@ -57,6 +57,20 @@ "when": 1786031744466, "tag": "0007_unusual_leader", "breakpoints": true + }, + { + "idx": 8, + "version": "7", + "when": 1786032811621, + "tag": "0008_exotic_fallen_one", + "breakpoints": true + }, + { + "idx": 9, + "version": "7", + "when": 1786033392652, + "tag": "0009_lovely_stingray", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/database/src/schema/_shared.ts b/packages/database/src/schema/_shared.ts index ed6ef50..c5c686e 100644 --- a/packages/database/src/schema/_shared.ts +++ b/packages/database/src/schema/_shared.ts @@ -46,7 +46,6 @@ import { UNITS, USER_ROLES, VERIFICATION_STATUSES, - TRUST_STATES, tuple, } from "@app/shared-types"; @@ -113,4 +112,3 @@ export const creatorLevelEnum = pgEnum("creator_level", tuple(CREATOR_LEVELS)); export const profileVisibilityEnum = pgEnum("profile_visibility", tuple(PROFILE_VISIBILITIES)); export const notificationTypeEnum = pgEnum("notification_type", tuple(NOTIFICATION_TYPES)); export const verificationStatusEnum = pgEnum("verification_status", tuple(VERIFICATION_STATUSES)); -export const trustStateEnum = pgEnum("trust_state", tuple(TRUST_STATES)); diff --git a/packages/database/src/schema/decay.ts b/packages/database/src/schema/decay.ts new file mode 100644 index 0000000..4b37a8e --- /dev/null +++ b/packages/database/src/schema/decay.ts @@ -0,0 +1,32 @@ +import { boolean, doublePrecision, integer, pgTable, text, uuid, varchar } from "drizzle-orm/pg-core"; +import { createdAt, updatedAt } from "./_shared.js"; +import { inventoryItems } from "./inventory.js"; + +/** + * Inventory Trust Engine (Fas 2 §5.2): deterministiska decay-profiler. + * En aktiv profil styr hur snabbt confidence sjunker med tiden. + * Decay påverkar endast FÖRTROENDE, inte ätbarhet (mjölkprincipen D-035). + */ +export const inventoryDecayProfiles = pgTable("inventory_decay_profiles", { + id: uuid("id").primaryKey().defaultRandom(), + name: varchar("name", { length: 64 }).notNull().unique(), + description: text("description"), + /** Antal dagar för confidence att halveras. */ + halfLifeDays: doublePrecision("half_life_days").notNull().default(7), + /** Efter detta antal dagar anses posten vara stale oavsett ursprunglig confidence. */ + staleAfterDays: integer("stale_after_days").notNull().default(30), + /** Sätt till false för att arkivera en profil utan att ta bort historiken. */ + active: boolean("active").notNull().default(true), + createdAt: createdAt(), + updatedAt: updatedAt(), +}); + +/** Valbar FK från inventory_items till den profil som användes när posten skapades. */ +export const inventoryItemDecayProfile = pgTable("inventory_item_decay_profile", { + inventoryItemId: uuid("inventory_item_id") + .primaryKey() + .references(() => inventoryItems.id, { onDelete: "cascade" }), + decayProfileId: uuid("decay_profile_id") + .notNull() + .references(() => inventoryDecayProfiles.id, { onDelete: "restrict" }), +}); diff --git a/packages/database/src/schema/index.ts b/packages/database/src/schema/index.ts index f8349c7..80f1ba4 100644 --- a/packages/database/src/schema/index.ts +++ b/packages/database/src/schema/index.ts @@ -5,6 +5,7 @@ export * from "./households.js"; export * from "./ingredients.js"; export * from "./products.js"; export * from "./inventory.js"; +export * from "./decay.js"; export * from "./recipes.js"; export * from "./translations.js"; export * from "./markets.js"; diff --git a/packages/database/src/schema/inventory.ts b/packages/database/src/schema/inventory.ts index 458c0e2..ed68b59 100644 --- a/packages/database/src/schema/inventory.ts +++ b/packages/database/src/schema/inventory.ts @@ -9,6 +9,7 @@ import { timestamp, uuid, integer, + varchar, } from "drizzle-orm/pg-core"; import type { NutritionDeclaration } from "@app/shared-types"; import { @@ -17,7 +18,6 @@ import { expiryStatusEnum, inventorySourceEnum, inventoryTransactionTypeEnum, - trustStateEnum, unitEnum, updatedAt, } from "./_shared.js"; @@ -64,7 +64,7 @@ export const inventoryItems = pgTable( lastVerifiedAt: timestamp("last_verified_at", { withTimezone: true }), expiryStatus: expiryStatusEnum("expiry_status").notNull().default("unknown"), /** Inventory Trust Engine state (Fas 2): computed from confidence, decay and verification. */ - trustState: trustStateEnum("trust_state").notNull().default("unverified"), + trustState: varchar("trust_state", { length: 16 }).notNull().default("unverified"), /** Sätts när saldot nått 0 och posten arkiverats. */ depletedAt: timestamp("depleted_at", { withTimezone: true }), modelVersion: text("model_version"), diff --git a/packages/database/src/seed/run.ts b/packages/database/src/seed/run.ts index 8dc0ee7..9d385fd 100644 --- a/packages/database/src/seed/run.ts +++ b/packages/database/src/seed/run.ts @@ -590,6 +590,19 @@ async function main() { } console.log(`[seed] ${flags.length} feature flags`); + // 7. Default trust decay profile (Fas 2 §5.2) + await db + .insert(schema.inventoryDecayProfiles) + .values({ + name: "default", + description: "Standardprofil: confidence halveras efter 7 dagar, stale efter 30 dagar.", + halfLifeDays: 7, + staleAfterDays: 30, + active: true, + }) + .onConflictDoNothing(); + console.log("[seed] 1 default decay profile"); + console.log("[seed] Klart."); await pool.end(); } diff --git a/packages/inventory-engine/src/trust.ts b/packages/inventory-engine/src/trust.ts index cce200e..9f93343 100644 --- a/packages/inventory-engine/src/trust.ts +++ b/packages/inventory-engine/src/trust.ts @@ -1,5 +1,10 @@ import type { TrustState } from "@app/shared-types"; +export interface DecayProfile { + halfLifeDays: number; + staleAfterDays: number; +} + export interface TrustInput { confidence: number; verifiedByUser: boolean; @@ -21,6 +26,23 @@ export interface TrustResult { score: number; // 0–100 } +const DEFAULT_DECAY: DecayProfile = { halfLifeDays: 7, staleAfterDays: 30 }; + +/** + * Deterministic exponential decay of confidence. + * Decay affects TRUST only, never edibility (mjölkprincipen D-035). + */ +export function applyDecay( + confidence: number, + elapsedDays: number, + profile: DecayProfile = DEFAULT_DECAY, +): number { + if (elapsedDays <= 0) return confidence; + if (elapsedDays >= profile.staleAfterDays) return 0; + const factor = Math.pow(0.5, elapsedDays / profile.halfLifeDays); + return Math.max(0, confidence * factor); +} + /** * Compute the trust state of an inventory item. * @@ -29,32 +51,40 @@ export interface TrustResult { * - decaying: confidence is dropping / not verified for a while. * - stale: very old, unverified, or confidence critically low. * - * Decay affects TRUST, not edibility (mjölkprincipen – spec D-035). + * Decay affects TRUST, not edibility (mjölkprincipen D-035). */ -export function computeTrust(input: TrustInput): TrustResult { - const now = Date.now(); +export function computeTrust( + input: TrustInput, + now: Date = new Date(), + profile: DecayProfile = DEFAULT_DECAY, +): TrustResult { + const nowMs = now.getTime(); const lastVerified = input.lastVerifiedAt ? new Date(input.lastVerifiedAt).getTime() : null; const updated = new Date(input.updatedAt).getTime(); - const daysSinceVerification = lastVerified ? (now - lastVerified) / 86_400_000 : null; - const daysSinceUpdate = (now - updated) / 86_400_000; + const referenceMs = lastVerified ?? updated; + const elapsedDays = (nowMs - referenceMs) / 86_400_000; + const daysSinceUpdate = (nowMs - updated) / 86_400_000; - // Base score from confidence (0–80 points) - let score = Math.round(input.confidence * 80); + // Decay confidence deterministically from the profile. + const effectiveConfidence = applyDecay(input.confidence, elapsedDays, profile); - // Verification bonus (0–20 points) - if (input.verifiedByUser && daysSinceVerification !== null && daysSinceVerification <= 7) { + // Base score from effective confidence (0–80 points) + let score = Math.round(effectiveConfidence * 80); + + // Verification bonus (0–20 points) – verification age is already reflected by decay, + // but a known user-verified item retains a small pedigree bonus. + if (input.verifiedByUser && elapsedDays <= 7) { score += 20; - } else if (input.verifiedByUser && daysSinceVerification !== null && daysSinceVerification <= 30) { + } else if (input.verifiedByUser && elapsedDays <= 30) { score += 10; } else if (input.verifiedByUser) { score += 5; } - // Decay penalties (only affect trust score) - if (daysSinceVerification !== null && daysSinceVerification > 7) { - score -= Math.min(20, Math.floor((daysSinceVerification - 7) / 7) * 5); - } - if (daysSinceUpdate > 14) { + // Additional staleness penalty when update is old. + if (daysSinceUpdate > profile.staleAfterDays) { + score -= 20; + } else if (daysSinceUpdate > 14) { score -= Math.min(15, Math.floor((daysSinceUpdate - 14) / 7) * 5); } if (input.quantity <= 0) { @@ -64,7 +94,7 @@ export function computeTrust(input: TrustInput): TrustResult { score = Math.max(0, Math.min(100, score)); let state: TrustState; - if (input.verifiedByUser && score >= 80) { + if (score >= 80) { state = "trusted"; } else if (score >= 60) { state = "trusted"; @@ -80,8 +110,12 @@ export function computeTrust(input: TrustInput): TrustResult { } /** Convenience wrapper that returns just the state for an inventory row. */ -export function itemTrustState(item: InventoryItemLike): TrustState { - return computeTrust(item).state; +export function itemTrustState( + item: InventoryItemLike, + now?: Date, + profile?: DecayProfile, +): TrustState { + return computeTrust(item, now, profile).state; } /** Pick the most urgent trust state from a list (used for household summary). */ diff --git a/packages/inventory-engine/test/trust.test.ts b/packages/inventory-engine/test/trust.test.ts index f3077ec..a979af9 100644 --- a/packages/inventory-engine/test/trust.test.ts +++ b/packages/inventory-engine/test/trust.test.ts @@ -1,54 +1,99 @@ import { describe, expect, it } from "vitest"; -import { computeTrust, worstTrustState } from "../src/trust.js"; +import { applyDecay, computeTrust, worstTrustState } from "../src/trust.js"; + +const PINNED = new Date("2026-08-06T12:00:00.000Z"); + +describe("applyDecay", () => { + it("returns original confidence when elapsed days is zero", () => { + expect(applyDecay(0.8, 0)).toBe(0.8); + }); + + it("halves confidence after halfLifeDays", () => { + expect(applyDecay(1.0, 7, { halfLifeDays: 7, staleAfterDays: 30 })).toBeCloseTo(0.5, 3); + }); + + it("returns zero when elapsed days reaches staleAfterDays", () => { + expect(applyDecay(0.8, 30, { halfLifeDays: 7, staleAfterDays: 30 })).toBe(0); + }); +}); describe("computeTrust", () => { - it("returns trusted for a fresh high-confidence item even without user verification", () => { - const result = computeTrust({ - confidence: 0.8, - verifiedByUser: false, - lastVerifiedAt: null, - quantity: 2, - updatedAt: new Date(), - }); + it("returns trusted for a fresh high-confidence item", () => { + const result = computeTrust( + { + confidence: 0.8, + verifiedByUser: false, + lastVerifiedAt: null, + quantity: 2, + updatedAt: PINNED, + }, + PINNED, + ); expect(result.state).toBe("trusted"); expect(result.score).toBeGreaterThanOrEqual(60); }); it("decays when last verification is old", () => { - const thirtyDaysAgo = new Date(Date.now() - 30 * 86_400_000); - const result = computeTrust({ - confidence: 0.6, - verifiedByUser: true, - lastVerifiedAt: thirtyDaysAgo, - quantity: 1, - updatedAt: thirtyDaysAgo, - }); + const sevenDaysAgo = new Date(PINNED.getTime() - 7 * 86_400_000); + const result = computeTrust( + { + confidence: 0.8, + verifiedByUser: true, + lastVerifiedAt: sevenDaysAgo, + quantity: 1, + updatedAt: sevenDaysAgo, + }, + PINNED, + ); expect(result.state).toBe("decaying"); }); it("is stale for very old unverified low-confidence items", () => { - const sixtyDaysAgo = new Date(Date.now() - 60 * 86_400_000); - const result = computeTrust({ - confidence: 0.2, - verifiedByUser: false, - lastVerifiedAt: null, - quantity: 1, - updatedAt: sixtyDaysAgo, - }); + const sixtyDaysAgo = new Date(PINNED.getTime() - 60 * 86_400_000); + const result = computeTrust( + { + confidence: 0.2, + verifiedByUser: false, + lastVerifiedAt: null, + quantity: 1, + updatedAt: sixtyDaysAgo, + }, + PINNED, + ); expect(result.state).toBe("stale"); }); it("is trusted when user verified recently", () => { - const result = computeTrust({ - confidence: 0.7, - verifiedByUser: true, - lastVerifiedAt: new Date(), - quantity: 1, - updatedAt: new Date(), - }); + const result = computeTrust( + { + confidence: 0.7, + verifiedByUser: true, + lastVerifiedAt: PINNED, + quantity: 1, + updatedAt: PINNED, + }, + PINNED, + ); expect(result.state).toBe("trusted"); expect(result.score).toBeGreaterThanOrEqual(70); }); + + it("uses custom decay profile to accelerate staleness", () => { + const tenDaysAgo = new Date(PINNED.getTime() - 10 * 86_400_000); + const fast = { halfLifeDays: 3, staleAfterDays: 10 }; + const result = computeTrust( + { + confidence: 0.9, + verifiedByUser: true, + lastVerifiedAt: tenDaysAgo, + quantity: 1, + updatedAt: tenDaysAgo, + }, + PINNED, + fast, + ); + expect(result.state).toBe("stale"); + }); }); describe("worstTrustState", () => {