diff --git a/.env.example b/.env.example index 9b0f6e5..8f431a6 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,3 @@ -# Contact me on discord for the credentials -DATABASE_URL=fakevalue \ No newline at end of file +# See docs for more information in `docs/database.md` +DB_TOKEN=fakevalue +DB_URL=fakevalue \ No newline at end of file diff --git a/.gitignore b/.gitignore index a942ee3..316e418 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,7 @@ node_modules /build .env -**/.DS_Store \ No newline at end of file +**/.DS_Store + +# Local database +db/local.db \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 14ee7cc..65af672 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,6 +2,16 @@ Fork and open a PR +## Get started + +### Local development + +```sh +npm run db:seed +npm install +npm run dev +``` + ## Commit message format We have very precise rules over how our Git commit messages must be formatted. This format leads to easier to read commit history. diff --git a/app/components/Card/Card.tsx b/app/components/Card/Card.tsx index 68266a1..2fc8610 100644 --- a/app/components/Card/Card.tsx +++ b/app/components/Card/Card.tsx @@ -1,11 +1,49 @@ -type CardPropsTypes = { +import clsx from "clsx"; +import { useState } from "react"; + +export type CardPropsTypes = { title: string; content: string; + id: number; + columnId: number; }; -export default function Card({ title, content }: CardPropsTypes) { +export default function Card({ title, content, id, columnId }: CardPropsTypes) { + const [isDragging, setIsDragging] = useState(false); + + const handleOnDragStart = (e: React.DragEvent) => { + e.dataTransfer.clearData(); + e.dataTransfer.effectAllowed = "move"; + e.dataTransfer.setData("application/hunters-card", id.toString()); + e.dataTransfer.setData("application/hunters-column", columnId.toString()); + setTimeout(() => { + setIsDragging(true); // This is a hack to make the "placeholder" card a different color + }, 0); + console.log("drag start for id: ", { id, columnId }); + }; + + const handleOnDragEnd = () => { + setIsDragging(false); + }; + + const handleOnDragOver = (e: React.DragEvent) => { + // To use later for ordering within the column + e.preventDefault(); + }; + return ( -
+

{title}

{content}

diff --git a/app/components/Column/Column.tsx b/app/components/Column/Column.tsx index 331e1b8..a65a9e9 100644 --- a/app/components/Column/Column.tsx +++ b/app/components/Column/Column.tsx @@ -1,13 +1,65 @@ -import Card from "~/components/Card/Card"; +import clsx from "clsx"; +import { useState } from "react"; +import Card, { CardPropsTypes } from "~/components/Card/Card"; +import { useSubmit } from "@remix-run/react"; + +type ColumnPropsTypes = { + cards: CardPropsTypes[]; + columnId: number; +}; + +export default function Column({ cards, columnId }: ColumnPropsTypes) { + const [isDraggedOver, setIsDraggedOver] = useState(false); + const [hasDrop, setHasDrop] = useState(false); + const submit = useSubmit(); + + const handleOnDrop = (e: React.DragEvent) => { + // Needed to be a droppable zone + e.preventDefault(); // Should add filtering to check if valid drop + const cardId = e.dataTransfer.getData("application/hunters-card"); + console.log("drop", { cardId, columnId }); + submit({ cardId, columnId }, { method: "POST", navigate: false }); + setIsDraggedOver(false); + setHasDrop(true); + setTimeout(() => { + setHasDrop(false); + }, 1000); + }; + + const handleOnDragOver = (e: React.DragEvent) => { + e.dataTransfer.setData("application/hunters-column", columnId.toString()); + + // Needed to be a droppable zone + e.preventDefault(); // Should add filtering to check if valid drop + setIsDraggedOver(true); + }; + + const handleOnDragLeave = (e: React.DragEvent) => { + e.dataTransfer.setData("application/hunters-column", ""); + setIsDraggedOver(false); + }; + + const handleOnDragEnter = (e: React.DragEvent) => { + e.dataTransfer.setData("application/hunters-column", columnId.toString()); + console.log("drag enter"); + }; -export default function Column() { return (
console.log("dragging over")} + className={clsx( + "border-1 min-h-48 rounded-md border border-solid border-gray-700 px-2 py-5", + isDraggedOver ? "bg-sky-50" : "bg-slate-500", + [hasDrop && "bg-red-400"], + )} + onDrop={handleOnDrop} + onDragOver={handleOnDragOver} + onDragLeave={handleOnDragLeave} + onDragEnter={handleOnDragEnter} >

This is a column

- + {cards.map((card) => ( + + ))}
); } diff --git a/app/db/db.server.ts b/app/db/db.server.ts new file mode 100644 index 0000000..8a9b494 --- /dev/null +++ b/app/db/db.server.ts @@ -0,0 +1,29 @@ +import { createClient, type Client } from "@libsql/client"; +import invariant from "invariant"; + +let client: Client; + +if (process.env.NODE_ENV === "production") { + console.log("🚀 Using production database"); + invariant( + process.env.DB_URL, + "DB_URL is not defined in the environment variables", + ); + + invariant( + process.env.DB_TOKEN, + "DB_TOKEN is not defined in the environment variables", + ); + + client = createClient({ + url: process.env.DB_URL, + authToken: process.env.DB_TOKEN, + }); +} else { + console.log("🚀 Using local database"); + client = createClient({ + url: "file:./db/local.db", + }); +} + +export default client; diff --git a/app/db/pool.server.ts b/app/db/pool.server.ts deleted file mode 100644 index 5924332..0000000 --- a/app/db/pool.server.ts +++ /dev/null @@ -1,9 +0,0 @@ -import mysql2 from "mysql2/promise"; -import invariant from "invariant"; - -invariant( - process.env.DATABASE_URL, - "DATABASE_URL is not defined in the environment variables", -); - -export default mysql2.createPool(process.env.DATABASE_URL); diff --git a/app/db/test.server.ts b/app/db/test.server.ts index 53f9cdf..adfd26b 100644 --- a/app/db/test.server.ts +++ b/app/db/test.server.ts @@ -1,6 +1,6 @@ -import pool from "./pool.server"; +import pool from "./db.server"; export async function getData() { - const [result] = await pool.execute("SELECT * FROM test"); - return result; + const { rows } = await pool.execute("SELECT * FROM test"); + return rows; } diff --git a/app/routes/board/queries.server.ts b/app/routes/board/queries.server.ts new file mode 100644 index 0000000..620df01 --- /dev/null +++ b/app/routes/board/queries.server.ts @@ -0,0 +1,41 @@ +import db from "~/db/db.server"; + +export async function getBoards() { + const { rows } = await db.execute("SELECT * FROM queries"); + return rows; +} + +export type ColumnType = { id: number; name: string }; +export type CardType = { id: number; name: string; columnId: number }; +export async function getCardsAndColumns() { + const { rows: column } = await db.execute({ + sql: ` + SELECT column.id, column.name FROM column + LEFT JOIN board ON column.boardId = board.id + WHERE board.id = ?; + `, + args: [1], + }); + + const { rows: card } = await db.execute({ + sql: ` + SELECT card.id, card.name, card.columnId FROM card + FULL JOIN column ON card.columnId = column.id + FULL JOIN board ON column.boardId = board.id + WHERE board.id = ?; + `, + args: [1], + }); + + return { column, card } as unknown as { + column: ColumnType[]; + card: CardType[]; + }; +} + +export async function mutateCardColumn(cardId: number, columnId: number) { + await db.execute({ + sql: "UPDATE card SET columnId = ? WHERE id = ?;", + args: [columnId, cardId], + }); +} diff --git a/app/routes/board/route.tsx b/app/routes/board/route.tsx index f13ee60..1c89346 100644 --- a/app/routes/board/route.tsx +++ b/app/routes/board/route.tsx @@ -1,13 +1,59 @@ import Column from "~/components/Column/Column"; +import { + type CardType, + type ColumnType, + getCardsAndColumns, + mutateCardColumn, +} from "./queries.server"; +import { useLoaderData } from "@remix-run/react"; +import { ActionFunctionArgs } from "@remix-run/node"; +import invariant from "invariant"; + +export async function loader() { + const data = await getCardsAndColumns(); + return data; +} + +export async function action({ request }: ActionFunctionArgs) { + const data = await request.formData(); + const cardId = data.get("cardId"); + invariant(cardId, "cardId is required"); + const columnId = data.get("columnId"); + invariant(columnId, "columnId is required"); + await mutateCardColumn( + parseInt(cardId.toString()), + parseInt(columnId.toString()), + ); + + return { ok: true }; +} + +function mapCardsToColumns(columns: ColumnType[], cards: CardType[]) { + return columns.map((column) => { + return { + ...column, + cards: cards.filter((card) => card.columnId === column.id), + }; + }); +} export default function Board() { + const { column, card } = useLoaderData(); + const columns = mapCardsToColumns(column, card); return (

Board

+

{JSON.stringify(columns, null, 4)}

- - - + {columns.map((column) => { + const cards = column.cards.map((card) => ({ + ...card, + title: card.name, + content: card.name, + id: card.id, + })); + return ; + })}
); diff --git a/db/seed.ts b/db/seed.ts new file mode 100644 index 0000000..afe849f --- /dev/null +++ b/db/seed.ts @@ -0,0 +1,110 @@ +import { unlink, writeFile, access } from "node:fs/promises"; +import { resolve } from "path"; + +/** + * This file should be updated on each migration to reflect the current state of the database. + * @todo Add unit tests for this file + */ +(async function seed() { + // Delete database file + const dbPath = resolve(process.cwd(), "db", "local.db"); + + try { + await access(dbPath); + await unlink(dbPath); + console.log("🗑️ Deleted local.db file"); + } catch (error) { + console.log("👀 Existing db not found"); + } + + await writeFile(dbPath, "", { flag: "w", mode: 0o666 }); + console.log("📝 Created brand new local.db file"); + + if (process.env.NODE_ENV === "production") { + console.log("🚨 Production environment detected. Exiting seed script."); + process.exit(0); + } + + // Async import of db module + const { default: db } = await import("~/db/db.server"); + + // Drop all tables in reverse order to avoid foreign key issues + await db.batch([ + `DROP TABLE IF EXISTS "card";`, + `DROP TABLE IF EXISTS "column";`, + `DROP TABLE IF EXISTS "board";`, + `DROP TABLE IF EXISTS "test";`, + ]); + + // Seeds `test` table - Temporary for _index.tsx route data loading + await db.batch([ + `CREATE TABLE "test" ("id" INTEGER PRIMARY KEY, "name" TEXT);`, + { + sql: `INSERT INTO "test" ("name") VALUES (?);`, + args: ["Welcome to 100hunters!"], + }, + ]); + console.log("🌱 Seeded test table"); + + await db.batch([ + `CREATE TABLE "board" ("id" INTEGER PRIMARY KEY, "name" TEXT);`, + { + sql: `INSERT INTO "board" ("name") VALUES (?);`, + args: ["board 1"], + }, + ]); + console.log("🌱 Seeded board table"); + + await db.batch([ + `CREATE TABLE "column" ( + "id" INTEGER PRIMARY KEY, + "name" TEXT, "boardId" INTEGER, + FOREIGN KEY ("boardId") REFERENCES "board" ("id"));`, + { + sql: `INSERT INTO "column" ("name", "boardId") VALUES (?, ?);`, + args: ["column 1", 1], + }, + { + sql: `INSERT INTO "column" ("name", "boardId") VALUES (?, ?);`, + args: ["column 2", 1], + }, + { + sql: `INSERT INTO "column" ("name", "boardId") VALUES (?, ?);`, + args: ["column 3", 1], + }, + ]); + console.log("🌱 Seeded column table"); + + await db.batch([ + `CREATE TABLE "card" ( + "id" INTEGER PRIMARY KEY, + "name" TEXT, + "columnId" INTEGER, + FOREIGN KEY ("columnId") REFERENCES "column" ("id"));`, + { + sql: `INSERT INTO "card" ("name", "columnId") VALUES (?, ?);`, + args: ["card 1-1", 1], + }, + { + sql: `INSERT INTO "card" ("name", "columnId") VALUES (?, ?);`, + args: ["card 2-1", 1], + }, + { + sql: `INSERT INTO "card" ("name", "columnId") VALUES (?, ?);`, + args: ["card 3-1", 1], + }, + { + sql: `INSERT INTO "card" ("name", "columnId") VALUES (?, ?);`, + args: ["card 1-2", 2], + }, + { + sql: `INSERT INTO "card" ("name", "columnId") VALUES (?, ?);`, + args: ["card 2-2", 2], + }, + { + sql: `INSERT INTO "card" ("name", "columnId") VALUES (?, ?);`, + args: ["card 3-2", 2], + }, + ]); + console.log("🌱 Seeded card table"); +})(); diff --git a/docs/database.md b/docs/database.md index 59af83b..972d1d0 100644 --- a/docs/database.md +++ b/docs/database.md @@ -1,18 +1,31 @@ ## Database + ### Decision -To practice SQL queries, we decided to NOT use an ORM. -Using prepare statements only for saftey reasons, see [here](https://sidorares.github.io/node-mysql2/docs#using-prepared-statements) +To practice SQL, we decided to not use an ORM. + +Locally we use an seeded SQLite database that you can create using `npm run db:seed`. +On production, we will use libSQL which is an open source fork of SQLite maintained by Turso. +The SDK supports both languages. + +If you want to use an hosted version of the database, you can follow the Turso Quickstart [guide](https://docs.turso.tech/quickstart), +setup the environment variables and run in production mode (`npm run build && npm run start`). + +### Transactions + +https://docs.turso.tech/sdk/ts/reference#batch-transactions ### Schema + Mermaid database schema ### Documentation + Schema should be kept up to date using Mermaid Markdown in `utils/mermaid/schema.mmd` ### Tools -`mysql2` https://sidorares.github.io/node-mysql2/docs -TypeScript + `mysql2` : https://sidorares.github.io/node-mysql2/docs/examples/typescript/row-data/index \ No newline at end of file + +-[`@libsql/client`](https://www.github.com/tursodatabase/libsql-client-ts/) diff --git a/environment.d.ts b/environment.d.ts index 60b64da..832b9b2 100644 --- a/environment.d.ts +++ b/environment.d.ts @@ -2,7 +2,8 @@ declare global { namespace NodeJS { interface ProcessEnv { NODE_ENV: "development" | "production"; - DATABASE_URL: string; + DB_URL: string; + DB_TOKEN: string; PORT?: string; } } diff --git a/package-lock.json b/package-lock.json index 7dad068..999217a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,6 +6,7 @@ "": { "name": "100hunters", "dependencies": { + "@libsql/client": "^0.5.6", "@remix-run/express": "^2.8.0", "@remix-run/node": "^2.8.0", "@remix-run/react": "^2.8.0", @@ -15,7 +16,6 @@ "invariant": "^2.2.4", "isbot": "^4.1.0", "morgan": "^1.10.0", - "mysql2": "^3.9.2", "react": "^18.2.0", "react-dom": "^18.2.0" }, @@ -3791,6 +3791,183 @@ "version": "2.0.1", "dev": true }, + "node_modules/@libsql/client": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@libsql/client/-/client-0.5.6.tgz", + "integrity": "sha512-UBjmDoxz75Z2sHdP+ETCROpeLA/77VMesiff8R4UWK1rnaWbh6/YoCLDILMJL3Rh0udQeKxjL8MjXthqohax+g==", + "dependencies": { + "@libsql/core": "^0.5.6", + "@libsql/hrana-client": "^0.5.6", + "js-base64": "^3.7.5", + "libsql": "^0.3.10" + } + }, + "node_modules/@libsql/core": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@libsql/core/-/core-0.5.6.tgz", + "integrity": "sha512-3vicUAydq6jPth410n4AsHHm1n2psTwvkSf94nfJlSXutGSZsl0updn2N/mJBgqUHkbuFoWZtlMifF0SwBj1xQ==", + "dependencies": { + "js-base64": "^3.7.5" + } + }, + "node_modules/@libsql/darwin-arm64": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@libsql/darwin-arm64/-/darwin-arm64-0.3.10.tgz", + "integrity": "sha512-RaexEFfPAFogd6dJlqkpCkTxdr6K14Z0286lodIJ8Ny77mWuWyBkWKxf70OYWXXAMxMJFUW+6al1F3/Osf/pTg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@libsql/darwin-x64": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@libsql/darwin-x64/-/darwin-x64-0.3.10.tgz", + "integrity": "sha512-SNVN6n4qNUdMW1fJMFmx4qn4n5RnXsxjFbczpkzG/V7m/5VeTFt1chhGcrahTHCr3+K6eRJWJUEQHRGqjBwPkw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@libsql/hrana-client": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@libsql/hrana-client/-/hrana-client-0.5.6.tgz", + "integrity": "sha512-mjQoAmejZ1atG+M3YR2ZW+rg6ceBByH/S/h17ZoYZkqbWrvohFhXyz2LFxj++ARMoY9m6w3RJJIRdJdmnEUlFg==", + "dependencies": { + "@libsql/isomorphic-fetch": "^0.1.12", + "@libsql/isomorphic-ws": "^0.1.5", + "js-base64": "^3.7.5", + "node-fetch": "^3.3.2" + } + }, + "node_modules/@libsql/hrana-client/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/@libsql/hrana-client/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/@libsql/isomorphic-fetch": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@libsql/isomorphic-fetch/-/isomorphic-fetch-0.1.12.tgz", + "integrity": "sha512-MRo4UcmjAGAa3ac56LoD5OE13m2p0lu0VEtZC2NZMcogM/jc5fU9YtMQ3qbPjFJ+u2BBjFZgMPkQaLS1dlMhpg==", + "dependencies": { + "@types/node-fetch": "^2.6.11", + "node-fetch": "^2.7.0" + } + }, + "node_modules/@libsql/isomorphic-ws": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@libsql/isomorphic-ws/-/isomorphic-ws-0.1.5.tgz", + "integrity": "sha512-DtLWIH29onUYR00i0GlQ3UdcTRC6EP4u9w/h9LxpUZJWRMARk6dQwZ6Jkd+QdwVpuAOrdxt18v0K2uIYR3fwFg==", + "dependencies": { + "@types/ws": "^8.5.4", + "ws": "^8.13.0" + } + }, + "node_modules/@libsql/isomorphic-ws/node_modules/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@libsql/linux-arm64-gnu": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@libsql/linux-arm64-gnu/-/linux-arm64-gnu-0.3.10.tgz", + "integrity": "sha512-2uXpi9d8qtyIOr7pyG4a88j6YXgemyIHEs2Wbp+PPletlCIPsFS+E7IQHbz8VwTohchOzcokGUm1Bc5QC+A7wg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/linux-arm64-musl": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@libsql/linux-arm64-musl/-/linux-arm64-musl-0.3.10.tgz", + "integrity": "sha512-72SN1FUavLvzHddCS861ynSpQndcW5oLGKA3U8CyMfgIZIwJAPc7+48Uj1plW00htXBx4GBpcntFp68KKIx3YQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/linux-x64-gnu": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@libsql/linux-x64-gnu/-/linux-x64-gnu-0.3.10.tgz", + "integrity": "sha512-hXyNqVRi7ONuyWZ1SX6setxL0QaQ7InyS3bHLupsi9s7NpOGD5vcpTaYicJOqmIIm+6kt8vJfmo7ZxlarIHy7Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/linux-x64-musl": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@libsql/linux-x64-musl/-/linux-x64-musl-0.3.10.tgz", + "integrity": "sha512-kNmIRxomVwt9S+cLyYS497F/3gXFF4r8wW12YSBQgxG75JYft07AHVd8J7HINg+oqRkLzT0s+mVX5dM6nk68EQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/win32-x64-msvc": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@libsql/win32-x64-msvc/-/win32-x64-msvc-0.3.10.tgz", + "integrity": "sha512-c/6rjdtGULKrJkLgfLobFefObfOtxjXGmCfPxv6pr0epPCeUEssfDbDIeEH9fQUgzogIMWEHwT8so52UJ/iT1Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@mdx-js/mdx": { "version": "2.3.0", "dev": true, @@ -3895,6 +4072,11 @@ "once": "^1.3.1" } }, + "node_modules/@neon-rs/load": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@neon-rs/load/-/load-0.0.4.tgz", + "integrity": "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "dev": true, @@ -6142,12 +6324,20 @@ }, "node_modules/@types/node": { "version": "20.11.24", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~5.26.4" } }, + "node_modules/@types/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, "node_modules/@types/normalize-package-data": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", @@ -6239,6 +6429,14 @@ "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", "dev": true }, + "node_modules/@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/yargs-parser": { "version": "21.0.3", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", @@ -7068,6 +7266,11 @@ "has-symbols": "^1.0.3" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/autoprefixer": { "version": "10.4.18", "dev": true, @@ -7877,6 +8080,17 @@ "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/comma-separated-tokens": { "version": "2.0.3", "dev": true, @@ -8417,12 +8631,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/denque": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "engines": { - "node": ">=0.10" + "node": ">=0.4.0" } }, "node_modules/depd": { @@ -8457,6 +8671,14 @@ "node": ">=8" } }, + "node_modules/detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-package-manager": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/detect-package-manager/-/detect-package-manager-2.0.1.tgz", @@ -10278,6 +10500,28 @@ "pend": "~1.2.0" } }, + "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/fetch-retry": { "version": "5.0.6", "resolved": "https://registry.npmjs.org/fetch-retry/-/fetch-retry-5.0.6.tgz", @@ -10534,6 +10778,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/format": { "version": "0.2.2", "dev": true, @@ -10541,6 +10798,17 @@ "node": ">=0.4.x" } }, + "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/forwarded": { "version": "0.2.0", "license": "MIT", @@ -10645,14 +10913,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/generate-function": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", - "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", - "dependencies": { - "is-property": "^1.0.2" - } - }, "node_modules/generic-names": { "version": "4.0.0", "dev": true, @@ -11742,11 +12002,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" - }, "node_modules/is-reference": { "version": "3.0.2", "dev": true, @@ -12056,6 +12311,11 @@ "jiti": "bin/jiti.js" } }, + "node_modules/js-base64": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz", + "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==" + }, "node_modules/js-tokens": { "version": "4.0.0", "license": "MIT" @@ -12286,6 +12546,34 @@ "node": ">= 0.8.0" } }, + "node_modules/libsql": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/libsql/-/libsql-0.3.10.tgz", + "integrity": "sha512-/8YMTbwWFPmrDWY+YFK3kYqVPFkMgQre0DGmBaOmjogMdSe+7GHm1/q9AZ61AWkEub/vHmi+bA4tqIzVhKnqzg==", + "cpu": [ + "x64", + "arm64", + "wasm32" + ], + "os": [ + "darwin", + "linux", + "win32" + ], + "dependencies": { + "@neon-rs/load": "^0.0.4", + "detect-libc": "2.0.2" + }, + "optionalDependencies": { + "@libsql/darwin-arm64": "0.3.10", + "@libsql/darwin-x64": "0.3.10", + "@libsql/linux-arm64-gnu": "0.3.10", + "@libsql/linux-arm64-musl": "0.3.10", + "@libsql/linux-x64-gnu": "0.3.10", + "@libsql/linux-x64-musl": "0.3.10", + "@libsql/win32-x64-msvc": "0.3.10" + } + }, "node_modules/lilconfig": { "version": "3.1.1", "dev": true, @@ -12416,11 +12704,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/long": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" - }, "node_modules/longest-streak": { "version": "3.1.0", "dev": true, @@ -13644,43 +13927,6 @@ "dev": true, "license": "MIT" }, - "node_modules/mysql2": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.9.2.tgz", - "integrity": "sha512-3Cwg/UuRkAv/wm6RhtPE5L7JlPB877vwSF6gfLAS68H+zhH+u5oa3AieqEd0D0/kC3W7qIhYbH419f7O9i/5nw==", - "dependencies": { - "denque": "^2.1.0", - "generate-function": "^2.3.1", - "iconv-lite": "^0.6.3", - "long": "^5.2.1", - "lru-cache": "^8.0.0", - "named-placeholders": "^1.1.3", - "seq-queue": "^0.0.5", - "sqlstring": "^2.3.2" - }, - "engines": { - "node": ">= 8.0" - } - }, - "node_modules/mysql2/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mysql2/node_modules/lru-cache": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz", - "integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==", - "engines": { - "node": ">=16.14" - } - }, "node_modules/mz": { "version": "2.7.0", "dev": true, @@ -13691,25 +13937,6 @@ "thenify-all": "^1.0.0" } }, - "node_modules/named-placeholders": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", - "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", - "dependencies": { - "lru-cache": "^7.14.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/named-placeholders/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "engines": { - "node": ">=12" - } - }, "node_modules/nanoid": { "version": "3.3.7", "dev": true, @@ -13779,11 +14006,28 @@ "node": "*" } }, + "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", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dev": true, "dependencies": { "whatwg-url": "^5.0.0" }, @@ -16319,11 +16563,6 @@ "version": "2.1.3", "license": "MIT" }, - "node_modules/seq-queue": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", - "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" - }, "node_modules/serve-static": { "version": "1.15.0", "license": "MIT", @@ -16516,14 +16755,6 @@ "node": ">= 10.x" } }, - "node_modules/sqlstring": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", - "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/ssri": { "version": "10.0.5", "dev": true, @@ -17270,8 +17501,7 @@ "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "node_modules/trim-lines": { "version": "3.0.1", @@ -17942,7 +18172,6 @@ }, "node_modules/undici-types": { "version": "5.26.5", - "dev": true, "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -18863,8 +19092,7 @@ "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/webpack-sources": { "version": "3.2.3", @@ -18885,7 +19113,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" diff --git a/package.json b/package.json index 410bc6b..477cf85 100644 --- a/package.json +++ b/package.json @@ -12,10 +12,12 @@ "format": "prettier . --write", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", + "db:seed": "tsx ./db/seed.ts", "db:gen-schema": "sh ./utils/mermaid/generate-schema.sh", "prepare": "husky" }, "dependencies": { + "@libsql/client": "^0.5.6", "@remix-run/express": "^2.8.0", "@remix-run/node": "^2.8.0", "@remix-run/react": "^2.8.0", @@ -25,7 +27,6 @@ "invariant": "^2.2.4", "isbot": "^4.1.0", "morgan": "^1.10.0", - "mysql2": "^3.9.2", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/utils/seed/index.ts b/utils/seed/index.ts deleted file mode 100644 index f3feeb5..0000000 --- a/utils/seed/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -// import pool from "~/db/pool.server"; -// import crypto from "crypto"; - -// const TABLE = "test"; - -// (async function seedDatabase() { -// console.log("Seeding database..."); -// const conn = await pool.getConnection(); - -// })();