diff --git a/.dev.vars.example b/.dev.vars.example index ece1d52a..4abd50ca 100644 --- a/.dev.vars.example +++ b/.dev.vars.example @@ -3,6 +3,6 @@ SUPABASE_KEY= SUPABASE_URL= BOT_ERROR_REPORTING_TOKEN= BOT_ERROR_REPORTING_USER_ID= - - - +DB_PASS= +DB_HOST= +DB_NAME= diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 6da40930..97395632 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -26,7 +26,8 @@ jobs: node-version: ${{ matrix.node-version }} cache: 'npm' - run: npm i - - run: npm run build --if-present + - run: npm run typecheck + - run: npm run build - run: npm run lint - run: npm run test:api - run: npm run test:frontend diff --git a/functions/add-card.ts b/functions/add-card.ts index 2a615126..b656321f 100644 --- a/functions/add-card.ts +++ b/functions/add-card.ts @@ -6,7 +6,6 @@ import { z } from "zod"; import { canEditDeck } from "./db/deck/can-edit-deck.ts"; import { envSchema } from "./env/env-schema.ts"; import { getDatabase } from "./db/get-database.ts"; -import { tables } from "./db/tables.ts"; import { DatabaseException } from "./db/database-exception.ts"; import { createForbiddenRequestResponse } from "./lib/json-response/create-forbidden-request-response.ts"; import { createJsonResponse } from "./lib/json-response/create-json-response.ts"; @@ -42,7 +41,7 @@ export const onRequestPost = handleError(async ({ request, env }) => { const db = getDatabase(envSafe); const { data } = input; - const createCardsResult = await db.from(tables.deckCard).insert({ + const createCardsResult = await db.from("deck_card").insert({ deck_id: data.deckId, front: data.card.front, back: data.card.back, diff --git a/functions/db/databaseTypes.ts b/functions/db/databaseTypes.ts new file mode 100644 index 00000000..60a554aa --- /dev/null +++ b/functions/db/databaseTypes.ts @@ -0,0 +1,242 @@ +export type Json = + | string + | number + | boolean + | null + | { [key: string]: Json | undefined } + | Json[]; + +export interface Database { + public: { + Tables: { + card_review: { + Row: { + card_id: number; + created_at: string; + interval: number; + last_review_date: string; + user_id: number; + }; + Insert: { + card_id: number; + created_at?: string; + interval: number; + last_review_date?: string; + user_id: number; + }; + Update: { + card_id?: number; + created_at?: string; + interval?: number; + last_review_date?: string; + user_id?: number; + }; + Relationships: [ + { + foreignKeyName: "card_review_card_id_fkey"; + columns: ["card_id"]; + referencedRelation: "deck_card"; + referencedColumns: ["id"]; + }, + { + foreignKeyName: "card_review_user_id_fkey"; + columns: ["user_id"]; + referencedRelation: "user"; + referencedColumns: ["id"]; + }, + ]; + }; + deck: { + Row: { + author_id: number | null; + created_at: string; + description: string | null; + id: number; + is_public: boolean; + name: string; + share_id: string | null; + }; + Insert: { + author_id?: number | null; + created_at?: string; + description?: string | null; + id?: number; + is_public?: boolean; + name: string; + share_id?: string | null; + }; + Update: { + author_id?: number | null; + created_at?: string; + description?: string | null; + id?: number; + is_public?: boolean; + name?: string; + share_id?: string | null; + }; + Relationships: [ + { + foreignKeyName: "deck_author_id_fkey"; + columns: ["author_id"]; + referencedRelation: "user"; + referencedColumns: ["id"]; + }, + ]; + }; + deck_card: { + Row: { + back: string; + created_at: string; + deck_id: number; + example: string | null; + front: string; + id: number; + updated_at: string; + }; + Insert: { + back: string; + created_at?: string; + deck_id: number; + example?: string | null; + front: string; + id?: number; + updated_at?: string; + }; + Update: { + back?: string; + created_at?: string; + deck_id?: number; + example?: string | null; + front?: string; + id?: number; + updated_at?: string; + }; + Relationships: [ + { + foreignKeyName: "deck_card_deck_id_fkey"; + columns: ["deck_id"]; + referencedRelation: "deck"; + referencedColumns: ["id"]; + }, + ]; + }; + user: { + Row: { + created_at: string; + first_name: string | null; + id: number; + is_admin: boolean; + language_code: string | null; + last_name: string | null; + last_reminded_date: string | null; + last_used: string | null; + username: string | null; + }; + Insert: { + created_at?: string; + first_name?: string | null; + id?: number; + is_admin?: boolean; + language_code?: string | null; + last_name?: string | null; + last_reminded_date?: string | null; + last_used?: string | null; + username?: string | null; + }; + Update: { + created_at?: string; + first_name?: string | null; + id?: number; + is_admin?: boolean; + language_code?: string | null; + last_name?: string | null; + last_reminded_date?: string | null; + last_used?: string | null; + username?: string | null; + }; + Relationships: []; + }; + user_deck: { + Row: { + created_at: string; + deck_id: number; + user_id: number; + }; + Insert: { + created_at?: string; + deck_id: number; + user_id: number; + }; + Update: { + created_at?: string; + deck_id?: number; + user_id?: number; + }; + Relationships: [ + { + foreignKeyName: "user_deck_deck_id_fkey"; + columns: ["deck_id"]; + referencedRelation: "deck"; + referencedColumns: ["id"]; + }, + { + foreignKeyName: "user_deck_user_id_fkey"; + columns: ["user_id"]; + referencedRelation: "user"; + referencedColumns: ["id"]; + }, + ]; + }; + }; + Views: { + [_ in never]: never; + }; + Functions: { + get_cards_to_review: { + Args: { + usr_id: number; + }; + Returns: { + id: number; + deck_id: number; + }[]; + }; + get_review_counts_per_user: { + Args: Record; + Returns: { + user_id: number; + review_count: number; + }[]; + }; + get_user_decks_deck_id: { + Args: { + usr_id: number; + }; + Returns: { + id: number; + }[]; + }; + get_users_with_review_to_notify: { + Args: Record; + Returns: { + user_id: number; + review_count: number; + }[]; + }; + get_users_with_review_to_notify3: { + Args: Record; + Returns: { + user_id: number; + review_count: number; + last_reminded_date: string; + }[]; + }; + }; + Enums: { + [_ in never]: never; + }; + CompositeTypes: { + [_ in never]: never; + }; + }; +} diff --git a/functions/db/deck/add-deck-to-mine-db.ts b/functions/db/deck/add-deck-to-mine-db.ts index f6270a80..9ff08385 100644 --- a/functions/db/deck/add-deck-to-mine-db.ts +++ b/functions/db/deck/add-deck-to-mine-db.ts @@ -1,6 +1,5 @@ import { EnvType } from "../../env/env-schema.ts"; import { getDatabase } from "../get-database.ts"; -import { tables } from "../tables.ts"; import { DatabaseException } from "../database-exception.ts"; export const addDeckToMineDb = async ( @@ -9,7 +8,7 @@ export const addDeckToMineDb = async ( ): Promise => { const db = getDatabase(env); - const { error } = await db.from(tables.userDeck).insert([body]).single(); + const { error } = await db.from("user_deck").insert([body]).single(); if (error) { throw new DatabaseException(error); diff --git a/functions/db/deck/can-edit-deck.ts b/functions/db/deck/can-edit-deck.ts index 94ee94ab..fdbc36f0 100644 --- a/functions/db/deck/can-edit-deck.ts +++ b/functions/db/deck/can-edit-deck.ts @@ -1,6 +1,5 @@ import { EnvType } from "../../env/env-schema.ts"; import { getDatabase } from "../get-database.ts"; -import { tables } from "../tables.ts"; import { DatabaseException } from "../database-exception.ts"; export const canEditDeck = async ( @@ -11,7 +10,7 @@ export const canEditDeck = async ( const db = getDatabase(envSafe); const canEditDeckResult = await db - .from(tables.deck) + .from("deck") .select() .eq("author_id", userId) .eq("id", deckId); diff --git a/functions/db/deck/get-cards-to-review-db.ts b/functions/db/deck/get-cards-to-review-db.ts index 989d226a..81563356 100644 --- a/functions/db/deck/get-cards-to-review-db.ts +++ b/functions/db/deck/get-cards-to-review-db.ts @@ -1,6 +1,5 @@ import { EnvType } from "../../env/env-schema.ts"; import { getDatabase } from "../get-database.ts"; -import { databaseFunctions } from "../tables.ts"; import { DatabaseException } from "../database-exception.ts"; import { z } from "zod"; @@ -19,7 +18,7 @@ export const getCardsToReviewDb = async ( ): Promise => { const db = getDatabase(env); - const result = await db.rpc(databaseFunctions.getCardsToReview, { + const result = await db.rpc("get_cards_to_review", { usr_id: userId, }); diff --git a/functions/db/deck/get-my-decks-db.ts b/functions/db/deck/get-my-decks-db.ts index 651cffbb..66aecea9 100644 --- a/functions/db/deck/get-my-decks-db.ts +++ b/functions/db/deck/get-my-decks-db.ts @@ -1,6 +1,5 @@ import { EnvType } from "../../env/env-schema.ts"; import { getDatabase } from "../get-database.ts"; -import { databaseFunctions, tables } from "../tables.ts"; import { DatabaseException } from "../database-exception.ts"; import { decksWithCardsSchema, @@ -14,7 +13,7 @@ export const getMyDecksDb = async ( ): Promise => { const db = getDatabase(env); - const getUserDeckIdsResult = await db.rpc(databaseFunctions.getUserDecks, { + const getUserDeckIdsResult = await db.rpc("get_user_decks_deck_id", { usr_id: userId, }); @@ -32,7 +31,7 @@ export const getMyDecksDb = async ( .parse(getUserDeckIdsResult.data); const { data, error } = await db - .from(tables.deck) + .from("deck") .select("*, deck_card!deck_card_deck_id_fkey(*)") .in("id", deckIds); diff --git a/functions/db/deck/get-public-decks-db.ts b/functions/db/deck/get-public-decks-db.ts index 205328cc..38178b32 100644 --- a/functions/db/deck/get-public-decks-db.ts +++ b/functions/db/deck/get-public-decks-db.ts @@ -1,6 +1,5 @@ import { EnvType } from "../../env/env-schema.ts"; import { getDatabase } from "../get-database.ts"; -import { tables } from "../tables.ts"; import { DatabaseException } from "../database-exception.ts"; import { decksWithCardsSchema, @@ -13,7 +12,7 @@ export const getPublicDecksDb = async ( const db = getDatabase(env); const { data, error } = await db - .from(tables.deck) + .from("deck") .select("*,deck_card!deck_card_deck_id_fkey(*)") .eq("is_public", true) .limit(20); diff --git a/functions/db/get-database.ts b/functions/db/get-database.ts index 4c3981db..401de1e7 100644 --- a/functions/db/get-database.ts +++ b/functions/db/get-database.ts @@ -1,6 +1,7 @@ import { EnvType } from "../env/env-schema.ts"; import { createClient } from "@supabase/supabase-js"; +import { Database } from "./databaseTypes.ts"; export const getDatabase = (env: EnvType) => { - return createClient(env.SUPABASE_URL, env.SUPABASE_KEY); + return createClient(env.SUPABASE_URL, env.SUPABASE_KEY); }; diff --git a/functions/db/tables.ts b/functions/db/tables.ts deleted file mode 100644 index f7e00473..00000000 --- a/functions/db/tables.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const tables = { - user: "user", - deck: "deck", - userDeck: "user_deck", - deckCard: "deck_card", - cardReview: "card_review", -} as const; - -export const databaseFunctions = { - getUserDecks: "get_user_decks_deck_id", - getCardsToReview: "get_cards_to_review", -} as const; diff --git a/functions/db/user/create-or-update-user-db.ts b/functions/db/user/create-or-update-user-db.ts index 557ea2a0..8664f386 100644 --- a/functions/db/user/create-or-update-user-db.ts +++ b/functions/db/user/create-or-update-user-db.ts @@ -1,5 +1,4 @@ import { getDatabase } from "../get-database.ts"; -import { tables } from "../tables.ts"; import { UserTelegramType } from "../../lib/telegram/validate-telegram-request.ts"; import { EnvType } from "../../env/env-schema.ts"; import { DatabaseException } from "../database-exception.ts"; @@ -22,14 +21,14 @@ export const createOrUpdateUserDb = async ( const db = getDatabase(env); const { data, error } = await db - .from(tables.user) + .from("user") .upsert({ id: user.id, first_name: user.firstName, last_name: user.lastName, language_code: user.languageCode, username: user.username, - last_used: new Date(), + last_used: new Date().toISOString(), }) .select(); diff --git a/functions/get-shared-deck.ts b/functions/get-shared-deck.ts index 3948ea35..e3875f57 100644 --- a/functions/get-shared-deck.ts +++ b/functions/get-shared-deck.ts @@ -3,7 +3,6 @@ import { createJsonResponse } from "./lib/json-response/create-json-response.ts" import { createBadRequestResponse } from "./lib/json-response/create-bad-request-response.ts"; import { envSchema } from "./env/env-schema.ts"; import { getDatabase } from "./db/get-database.ts"; -import { tables } from "./db/tables.ts"; import { DatabaseException } from "./db/database-exception.ts"; import { createNotFoundResponse } from "./lib/json-response/create-not-found-response.ts"; import { @@ -28,7 +27,7 @@ export const onRequest = handleError(async ({ env, request }) => { const db = getDatabase(envSafe); const result = await db - .from(tables.deck) + .from("deck") .select("*, deck_card!deck_card_deck_id_fkey(*)") .eq("share_id", shareId); diff --git a/functions/review-cards.ts b/functions/review-cards.ts index 5327258e..68cb97e2 100644 --- a/functions/review-cards.ts +++ b/functions/review-cards.ts @@ -5,7 +5,6 @@ import { z } from "zod"; import { createBadRequestResponse } from "./lib/json-response/create-bad-request-response.ts"; import { envSchema } from "./env/env-schema.ts"; import { getDatabase } from "./db/get-database.ts"; -import { tables } from "./db/tables.ts"; import { reviewCard } from "./services/review-card.ts"; import { DateTime } from "luxon"; import { DatabaseException } from "./db/database-exception.ts"; @@ -36,7 +35,7 @@ export const onRequestPost = handleError(async ({ env, request }) => { const db = getDatabase(envSafe); const { data: existingReviews, error } = await db - .from(tables.cardReview) + .from("card_review") .select("card_id, interval") .eq("user_id", user.id) .in( @@ -51,7 +50,7 @@ export const onRequestPost = handleError(async ({ env, request }) => { const now = DateTime.now(); const upsertReviewsResult = await db - .from(tables.cardReview) + .from("card_review") .upsert( input.data.cards.map((card) => { const previousInterval = existingReviews.find( @@ -68,7 +67,7 @@ export const onRequestPost = handleError(async ({ env, request }) => { return { user_id: user.id, card_id: card.id, - last_review_date: now.toJSDate(), + last_review_date: now.toJSDate().toISOString(), interval: reviewResult.interval, }; }), diff --git a/functions/share-deck.ts b/functions/share-deck.ts index 5d02ec88..ba99ed0b 100644 --- a/functions/share-deck.ts +++ b/functions/share-deck.ts @@ -6,7 +6,6 @@ import { createAuthFailedResponse } from "./lib/json-response/create-auth-failed import { createBadRequestResponse } from "./lib/json-response/create-bad-request-response.ts"; import { envSchema } from "./env/env-schema.ts"; import { getDatabase } from "./db/get-database.ts"; -import { tables } from "./db/tables.ts"; import { DatabaseException } from "./db/database-exception.ts"; import { createJsonResponse } from "./lib/json-response/create-json-response.ts"; @@ -33,7 +32,7 @@ export const onRequestPost = handleError(async ({ env, request }) => { const db = getDatabase(envSafe); const getDeckQuery = await db - .from(tables.deck) + .from("deck") .select() .eq("id", input.data.deckId) .not("share_id", "is", null); @@ -53,7 +52,7 @@ export const onRequestPost = handleError(async ({ env, request }) => { const shareId = new ShortUniqueId({ length: 10 }).rnd(); const { error } = await db - .from(tables.deck) + .from("deck") .update({ share_id: shareId }) .eq("id", input.data.deckId); diff --git a/functions/upsert-deck.ts b/functions/upsert-deck.ts index 80fa177a..c4f0b663 100644 --- a/functions/upsert-deck.ts +++ b/functions/upsert-deck.ts @@ -5,7 +5,6 @@ import { createAuthFailedResponse } from "./lib/json-response/create-auth-failed import { createBadRequestResponse } from "./lib/json-response/create-bad-request-response.ts"; import { envSchema } from "./env/env-schema.ts"; import { getDatabase } from "./db/get-database.ts"; -import { tables } from "./db/tables.ts"; import { DatabaseException } from "./db/database-exception.ts"; import { createJsonResponse } from "./lib/json-response/create-json-response.ts"; import { deckSchema } from "./db/deck/decks-with-cards-schema.ts"; @@ -50,7 +49,7 @@ export const onRequestPost = handleError(async ({ request, env }) => { } const upsertDeckResult = await db - .from(tables.deck) + .from("deck") .upsert({ id: input.data.id ? input.data.id : undefined, author_id: user.id, @@ -67,11 +66,11 @@ export const onRequestPost = handleError(async ({ request, env }) => { // Supabase returns an array as a result of upsert, that's why it gets validated against an array here const upsertedDecks = z.array(deckSchema).parse(upsertDeckResult.data); - const updateCardsResult = await db.from(tables.deckCard).upsert( + const updateCardsResult = await db.from("deck_card").upsert( input.data.cards .filter((card) => card.id) .map((card) => ({ - id: card.id, + id: card.id ?? undefined, deck_id: upsertedDecks[0].id, front: card.front, back: card.back, @@ -82,7 +81,7 @@ export const onRequestPost = handleError(async ({ request, env }) => { throw new DatabaseException(updateCardsResult.error); } - const createCardsResult = await db.from(tables.deckCard).insert( + const createCardsResult = await db.from("deck_card").insert( input.data.cards .filter((card) => !card.id) .map((card) => ({ diff --git a/package-lock.json b/package-lock.json index dde29811..5ae27b14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "react-hotkeys-hook": "^4.4.1", "react-lines-ellipsis": "^0.15.3", "short-unique-id": "^5.0.3", + "supabase": "^1.110.1", "zod": "^3.22.3" }, "devDependencies": { @@ -1914,6 +1915,17 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2030,6 +2042,20 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/bin-links": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-4.0.3.tgz", + "integrity": "sha512-obsRaULtJurnfox/MDwgq6Yo9kzbv1CPTk/1/s7Z/61Lezc8IKkFCOXNeVLXz0456WRzBQmSsDWlai2tIhBsfA==", + "dependencies": { + "cmd-shim": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "read-cmd-shim": "^4.0.0", + "write-file-atomic": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -2258,6 +2284,22 @@ "node": ">= 6" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/cmd-shim": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-6.0.2.tgz", + "integrity": "sha512-+FFYbB0YLaAkhkcrjkyNLYDiOsFSfRjwjY19LXk/psmMx1z00xlCv7hhQoTGXXIKi+YXHL/iiFo8NqMVQX9nOw==", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -2867,6 +2909,28 @@ "reusify": "^1.0.4" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -2932,6 +2996,17 @@ "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/framer-motion": { "version": "10.16.4", "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.16.4.tgz", @@ -2955,6 +3030,33 @@ } } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3112,6 +3214,18 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "node_modules/https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -3140,7 +3254,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, "engines": { "node": ">=0.8.19" } @@ -3575,6 +3688,53 @@ "node": "*" } }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/mlly": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.4.2.tgz", @@ -3671,6 +3831,24 @@ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -3724,6 +3902,14 @@ "node": ">=0.10.0" } }, + "node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", + "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4096,6 +4282,14 @@ "node": ">=0.10.0" } }, + "node_modules/read-cmd-shim": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-4.0.0.tgz", + "integrity": "sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q==", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -4320,6 +4514,17 @@ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -4446,6 +4651,49 @@ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" }, + "node_modules/supabase": { + "version": "1.110.1", + "resolved": "https://registry.npmjs.org/supabase/-/supabase-1.110.1.tgz", + "integrity": "sha512-pQVfbs/n8ZBDuSDv6YJKIH1Uh/QBRxjp6pLW52YkKgjgfwndlKwKuJoPiuWDxBkRG1QXxmCHi3Hk+JeNx9/FRg==", + "hasInstallScript": true, + "dependencies": { + "bin-links": "^4.0.1", + "https-proxy-agent": "^7.0.2", + "node-fetch": "^3.2.10", + "tar": "6.2.0" + }, + "bin": { + "supabase": "bin/supabase" + }, + "engines": { + "npm": ">=8" + } + }, + "node_modules/supabase/node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/supabase/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -4468,6 +4716,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tar": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", + "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -4871,6 +5140,14 @@ "node": ">=12" } }, + "node_modules/web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "engines": { + "node": ">= 8" + } + }, "node_modules/webcrypto-core": { "version": "1.7.7", "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.7.7.tgz", @@ -5404,6 +5681,18 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/ws": { "version": "8.14.2", "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", diff --git a/package.json b/package.json index 39ca30f9..694bd42b 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "version": "0.0.0", "type": "module", "scripts": { - "build": "tsc && vite build", + "build": "vite build", + "typecheck": "npx tsc", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "dev:frontend:start": "vite", "dev:api:start": "npx wrangler pages dev /functions --compatibility-date=2023-09-22", @@ -14,7 +15,8 @@ "test:frontend": "npx vitest --dir src/", "test:frontend:coverage": "npx vitest run --dir src/ --coverage", "prod:api:logs": "npx wrangler pages deployment tail", - "prod:deploy": "npx wrangler pages publish functions --project-name=memo-card" + "prod:deploy": "npx wrangler pages publish functions --project-name=memo-card", + "generate-db-types": "export $(cat .dev.vars | xargs) && npx supabase gen types typescript --db-url postgresql://postgres:$DB_PASS@$DB_HOST:5432/$DB_NAME >./functions/db/databaseTypes.ts" }, "dependencies": { "@emotion/css": "^11.11.2", @@ -36,6 +38,7 @@ "react-hotkeys-hook": "^4.4.1", "react-lines-ellipsis": "^0.15.3", "short-unique-id": "^5.0.3", + "supabase": "^1.110.1", "zod": "^3.22.3" }, "devDependencies": {