From 6bb1ecd00e4b2f0438b13da8876058ed098a0e6e Mon Sep 17 00:00:00 2001 From: Tirth Bodawala Date: Thu, 2 Jan 2025 19:39:28 +0530 Subject: [PATCH] Change from Astroflare to Flarekit Update the database package structure as I was not happy with the previous implementation. A singleton instance should be created for database init along with context of cache, queue if needs to be used for caching purpose. Thus to avoid complexity of the future we have updated the code for @flarekit/database Change cloudflare worker to Hapi worker handling the queue, schedule and api request. Change the gallery function. However this is not yet fully functional, rewamp is needed for Astro. --- .github/workflows/deploy-web.yml | 2 +- .vscode/settings.json | 7 +- README.md | 38 +- apps/api/.gitignore | 34 + apps/api/README.md | 8 + apps/api/eslint.config.js | 3 + apps/{worker => api}/package.json | 22 +- apps/api/src/index.ts | 46 + apps/api/tsconfig.json | 15 + .../vitest.config.ts} | 0 .../{worker => api}/worker-configuration.d.ts | 0 apps/{worker => api}/wrangler.config.json | 6 +- apps/web/README.md | 14 +- apps/web/astro.config.mjs | 2 +- apps/web/eslint.config.js | 8 +- apps/web/package.json | 9 +- apps/web/src/env.d.ts | 18 +- apps/web/src/middleware.ts | 9 +- apps/web/src/pages/gallery/index.astro | 6 +- apps/web/src/pages/upload.astro | 1 - apps/web/src/utils/upload.util.ts | 39 +- apps/web/tsconfig.json | 8 +- apps/web/wrangler.config.json | 2 +- apps/worker/.editorconfig | 12 - apps/worker/.gitignore | 176 ---- apps/worker/src/index.ts | 63 -- apps/worker/test/index.spec.ts | 26 - apps/worker/test/tsconfig.json | 11 - apps/worker/tsconfig.json | 44 - eslint.config.js | 53 +- package-lock.json | 793 +++++++++--------- package.json | 10 +- packages/database/.gitignore | 34 + packages/database/eslint.config.js | 3 + packages/database/package.json | 23 +- packages/database/rollup.config.js | 23 - packages/database/src/@types/database.type.ts | 3 - .../database/src/__tests__/client.e2e.test.ts | 49 -- .../schema/storage.schema.e2e.test.ts | 179 ---- .../src/__tests__/scripts/global-setup.ts | 41 - .../src/__tests__/scripts/migration-runner.ts | 20 - .../database/src/__tests__/scripts/utils.ts | 10 - .../services/storage.service.e2e.test.ts | 308 ------- packages/database/src/client.ts | 16 - packages/database/src/index.ts | 29 +- packages/database/src/proxy.ts | 59 ++ packages/database/src/schemas.ts | 1 + packages/database/src/services.ts | 3 + .../database/src/services/storage.service.ts | 20 +- packages/database/src/types.ts | 8 + packages/database/test/apply-migrations.ts | 6 + packages/database/test/env.d.ts | 5 + packages/database/test/index.test.ts | 37 + packages/database/test/proxy.test.ts | 53 ++ .../test/services/storage.service.test.ts | 217 +++++ packages/database/test/tsconfig.json | 12 + packages/database/tsconfig.json | 14 +- packages/database/tsconfig.test.json | 6 - packages/database/vitest.config.ts | 56 +- packages/database/worker-configuration.d.ts | 9 + packages/database/wrangler.config.json | 4 + turbo.json | 20 +- wrangler.json | 4 +- 63 files changed, 1186 insertions(+), 1571 deletions(-) create mode 100644 apps/api/.gitignore create mode 100644 apps/api/README.md create mode 100644 apps/api/eslint.config.js rename apps/{worker => api}/package.json (56%) create mode 100644 apps/api/src/index.ts create mode 100644 apps/api/tsconfig.json rename apps/{worker/vitest.config.mts => api/vitest.config.ts} (100%) rename apps/{worker => api}/worker-configuration.d.ts (100%) rename apps/{worker => api}/wrangler.config.json (78%) delete mode 100644 apps/worker/.editorconfig delete mode 100644 apps/worker/.gitignore delete mode 100644 apps/worker/src/index.ts delete mode 100644 apps/worker/test/index.spec.ts delete mode 100644 apps/worker/test/tsconfig.json delete mode 100644 apps/worker/tsconfig.json create mode 100644 packages/database/.gitignore create mode 100644 packages/database/eslint.config.js delete mode 100644 packages/database/src/@types/database.type.ts delete mode 100644 packages/database/src/__tests__/client.e2e.test.ts delete mode 100644 packages/database/src/__tests__/schema/storage.schema.e2e.test.ts delete mode 100644 packages/database/src/__tests__/scripts/global-setup.ts delete mode 100644 packages/database/src/__tests__/scripts/migration-runner.ts delete mode 100644 packages/database/src/__tests__/scripts/utils.ts delete mode 100644 packages/database/src/__tests__/services/storage.service.e2e.test.ts delete mode 100644 packages/database/src/client.ts create mode 100644 packages/database/src/proxy.ts create mode 100644 packages/database/src/schemas.ts create mode 100644 packages/database/src/services.ts create mode 100644 packages/database/src/types.ts create mode 100644 packages/database/test/apply-migrations.ts create mode 100644 packages/database/test/env.d.ts create mode 100644 packages/database/test/index.test.ts create mode 100644 packages/database/test/proxy.test.ts create mode 100644 packages/database/test/services/storage.service.test.ts create mode 100644 packages/database/test/tsconfig.json delete mode 100644 packages/database/tsconfig.test.json create mode 100644 packages/database/worker-configuration.d.ts create mode 100644 packages/database/wrangler.config.json diff --git a/.github/workflows/deploy-web.yml b/.github/workflows/deploy-web.yml index 7865f0a..d5d171f 100644 --- a/.github/workflows/deploy-web.yml +++ b/.github/workflows/deploy-web.yml @@ -198,7 +198,7 @@ jobs: run: npm run setup - name: Run Setup - run: npm run build --workspace=@services/database + run: npm run build --workspace=@flarekit/database - name: Deploy to Cloudflare Workers uses: cloudflare/wrangler-action@v3 diff --git a/.vscode/settings.json b/.vscode/settings.json index 0407bb1..876e3ed 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,10 @@ { - "coverage-gutters.coverageFilePaths": ["./coverage/lcov.info"], + "coverage-gutters.coverageFileNames": [ + "clover.xml", + "lcov.info", + "coverage.xml", + "cov.xml" + ], "coverage-gutters.showGutterCoverage": true, "editor.formatOnSave": true, "eslint.validate": [ diff --git a/README.md b/README.md index 6d3fbd1..efe6fcb 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@

- Astroflare

-# **Astroflare** +# **Flarekit** -Astroflare is a scalable and modular monorepo ecosystem for building modern, edge-first web applications using **Astro** and **Cloudflare Workers**. Itโ€™s designed to simplify the development of performant, globally distributed applications while promoting code reusability and maintainability. +Flarekit is a scalable and modular monorepo ecosystem for building modern, edge-first web applications using **Astro** and **Cloudflare Workers**. Itโ€™s designed to simplify the development of performant, globally distributed applications while promoting code reusability and maintainability. This project is supported and sponsored by [Atyantik Technologies](https://atyantik.com/). ๐Ÿš€ @@ -37,7 +37,7 @@ This project is supported and sponsored by [Atyantik Technologies](https://atyan ## **Current Structure** -Astroflare is currently focused on providing: +Flarekit is currently focused on providing: - A **web application** built with Astro. - A basic structure for future apps and services. @@ -45,9 +45,9 @@ Astroflare is currently focused on providing: ### **Directory Layout** ``` -astroflare/ +flarekit/ โ”œโ”€โ”€ apps/ -โ”‚ โ””โ”€โ”€ web/ # Astro-based frontend (@apps/web) +โ”‚ โ””โ”€โ”€ web/ # Astro-based frontend (@flarekit/web) โ”œโ”€โ”€ packages/ โ”‚ โ””โ”€โ”€ db/ # Database schema and utilities (@services/db) โ”œโ”€โ”€ turbo.json # Turborepo configuration @@ -57,19 +57,19 @@ astroflare/ ### **Available Packages** -| Package | Description | -| -------------- | ---------------------------------------------- | -| `@apps/web` | The primary web frontend built with Astro. | -| `@services/db` | Database utilities and schema for the project. | +| Package | Description | +| --------------- | ---------------------------------------------- | +| `@flarekit/web` | The primary web frontend built with Astro. | +| `@services/db` | Database utilities and schema for the project. | --- ## **Future Scope** -Astroflare is designed with extensibility in mind. Future plans include: +Flarekit is designed with extensibility in mind. Future plans include: - **New Applications**: - - `@apps/api`: Cloudflare Worker-based API services. + - `@flarekit/api`: Cloudflare Worker-based API services. - **Reusable Services**: - `@services/auth`: Authentication utilities and middleware. - `@services/utils`: Shared utility functions for applications. @@ -84,8 +84,8 @@ These features will be introduced incrementally, and contributions are welcome! ### **1. Clone the Repository** ```bash -git clone https://github.com/Atyantik/astroflare.git -cd astroflare +git clone https://github.com/Atyantik/flarekit.git +cd flarekit ``` ### **2. Install Dependencies** @@ -133,7 +133,7 @@ Each app or package may have its own configuration file. For example: ## **Contributing** -Weโ€™re building Astroflare to support modern, scalable web development. Contributions are welcome to help improve its features and extend its scope. +Weโ€™re building Flarekit to support modern, scalable web development. Contributions are welcome to help improve its features and extend its scope. ### **How to Contribute** @@ -156,13 +156,13 @@ Weโ€™re building Astroflare to support modern, scalable web development. Contrib ## **License** -Astroflare is licensed under the [MIT License](LICENSE). +Flarekit is licensed under the [MIT License](LICENSE). --- ## **About Atyantik Technologies** -Astroflare is proudly supported and sponsored by **[Atyantik Technologies](https://atyantik.com)**, a leading software development company specializing in scalable web applications, cloud services, and cutting-edge technologies. +Flarekit is proudly supported and sponsored by **[Atyantik Technologies](https://atyantik.com)**, a leading software development company specializing in scalable web applications, cloud services, and cutting-edge technologies. ### **Contact Atyantik** @@ -176,10 +176,10 @@ Astroflare is proudly supported and sponsored by **[Atyantik Technologies](https --- -Astroflare โ€“ Simplifying Edge-First Development with Cloudflare ๐ŸŒโœจ +Flarekit โ€“ Simplifying Edge-First Development with Cloudflare ๐ŸŒโœจ --- ### **Feedback and Support** -If you have suggestions or run into issues, please [open an issue](https://github.com/Atyantik/astroflare/issues) or contact us directly. We value your feedback and contributions! +If you have suggestions or run into issues, please [open an issue](https://github.com/Atyantik/flarekit/issues) or contact us directly. We value your feedback and contributions! diff --git a/apps/api/.gitignore b/apps/api/.gitignore new file mode 100644 index 0000000..761af54 --- /dev/null +++ b/apps/api/.gitignore @@ -0,0 +1,34 @@ +# prod +dist/ + +# dev +.yarn/ +!.yarn/releases +.vscode/* +!.vscode/launch.json +!.vscode/*.code-snippets +.idea/workspace.xml +.idea/usage.statistics.xml +.idea/shelf + +# deps +node_modules/ +.wrangler + +# env +.env +.env.production +.dev.vars +wrangler.json + +# logs +logs/ +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +# misc +.DS_Store diff --git a/apps/api/README.md b/apps/api/README.md new file mode 100644 index 0000000..cc58e96 --- /dev/null +++ b/apps/api/README.md @@ -0,0 +1,8 @@ +``` +npm install +npm run dev +``` + +``` +npm run deploy +``` diff --git a/apps/api/eslint.config.js b/apps/api/eslint.config.js new file mode 100644 index 0000000..71ba4b8 --- /dev/null +++ b/apps/api/eslint.config.js @@ -0,0 +1,3 @@ +import rootConfig from '../../eslint.config.js'; + +export default [...rootConfig]; diff --git a/apps/worker/package.json b/apps/api/package.json similarity index 56% rename from apps/worker/package.json rename to apps/api/package.json index c8a2811..85af36f 100644 --- a/apps/worker/package.json +++ b/apps/api/package.json @@ -1,22 +1,20 @@ { - "name": "@apps/worker", - "version": "1.0.0", - "private": true, + "name": "@flarekit/api", "scripts": { - "deploy": "npm run setup && wrangler deploy", "dev": "npm run setup && wrangler dev --test-scheduled --persist-to=../../.wrangler/state", - "start": "npm run setup && wrangler dev", + "deploy": "npm run setup && wrangler deploy", "test": "npm run setup && CI=true vitest run", "setup": "node ../../scripts/generate-wrangler.json.js && wrangler types" }, + "dependencies": { + "@flarekit/database": "*", + "hono": "^4.6.15" + }, "devDependencies": { - "@cloudflare/vitest-pool-workers": "^0.5.2", - "@cloudflare/workers-types": "^4.20241224.0", - "typescript": "^5.5.2", - "vitest": "2.1.8", + "@cloudflare/vitest-pool-workers": "^0.5.40", + "@cloudflare/workers-types": "^4.20241230.0", + "typescript": "^5.7.2", + "vitest": "^2.1.8", "wrangler": "^3.99.0" - }, - "dependencies": { - "@services/database": "*" } } diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts new file mode 100644 index 0000000..c025943 --- /dev/null +++ b/apps/api/src/index.ts @@ -0,0 +1,46 @@ +import { Handler, Hono } from 'hono'; +// import { clearStorageRecords, getDBClient, listStorageRecords } from '@flarekit/database'; + +const app = new Hono<{ Bindings: Env }>(); + +const honoHomeRoute: Handler = (c) => { + return c.json({ + message: 'Welcome to Hono!', + }); +}; +app.get('/', honoHomeRoute); + +export default { + fetch: app.fetch, + /* istanbul ignore next: Cannot test Queue invocation */ + // async queue( batch: MessageBatch, env: Environment, ctx: ExecutionContext) + async queue(batch): Promise { + let messages = JSON.stringify(batch.messages); + console.log(`Consumed from our queue: ${messages}`); + batch.ackAll(); + }, + + /* istanbul ignore next: Cannot test scheduled invocation */ + // scheduled(event: ScheduledEvent, env: Environment, ctx: ExecutionContext) + async scheduled(event, env, ctx) { + // Pass a promise + ctx.waitUntil( + (async () => { + // Clear the storage every 5th minute + // if (event.cron.startsWith('*/5')) { + // const DB = await getDBClient(this, env.DB); + // const STORAGE = env.STORAGE; + // const CACHE = env.CACHE; + // // Get all storage Records + // const storageRecords = await listStorageRecords(DB); + // // Remove each storage record from + // for (const record of storageRecords) { + // await STORAGE.delete(record.key); + // } + // await clearStorageRecords(DB); + // await CACHE.delete('storage_records'); + // } + })(), + ); + }, +} satisfies ExportedHandler; diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json new file mode 100644 index 0000000..628cb94 --- /dev/null +++ b/apps/api/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "skipLibCheck": true, + "lib": ["ESNext"], + "types": ["@cloudflare/workers-types"], + "jsx": "react-jsx", + "jsxImportSource": "hono/jsx" + }, + "exclude": ["test"], + "include": ["worker-configuration.d.ts", "src/**/*.ts"] +} diff --git a/apps/worker/vitest.config.mts b/apps/api/vitest.config.ts similarity index 100% rename from apps/worker/vitest.config.mts rename to apps/api/vitest.config.ts diff --git a/apps/worker/worker-configuration.d.ts b/apps/api/worker-configuration.d.ts similarity index 100% rename from apps/worker/worker-configuration.d.ts rename to apps/api/worker-configuration.d.ts diff --git a/apps/worker/wrangler.config.json b/apps/api/wrangler.config.json similarity index 78% rename from apps/worker/wrangler.config.json rename to apps/api/wrangler.config.json index 3af3ef5..15af0a2 100644 --- a/apps/worker/wrangler.config.json +++ b/apps/api/wrangler.config.json @@ -1,6 +1,6 @@ { "$schema": "../../node_modules/wrangler/config-schema.json", - "name": "astroflare-worker", + "name": "flarekit-backend", "main": "src/index.ts", "workers_dev": false, "observability": { @@ -13,7 +13,7 @@ "queues": { "consumers": [ { - "queue": "astroflare-queue", + "queue": "flarekit-queue", "max_batch_size": 10, "max_batch_timeout": 5 } @@ -21,7 +21,7 @@ }, "routes": [ { - "pattern": "astroflare-api.atyantik.com", + "pattern": "flarekit-api.atyantik.com", "custom_domain": true } ] diff --git a/apps/web/README.md b/apps/web/README.md index e7ce0f5..28ae9aa 100644 --- a/apps/web/README.md +++ b/apps/web/README.md @@ -1,6 +1,6 @@ -# **Astroflare Web App** +# **Flarekit Web App** -This repository contains the **Astroflare Web App**, a high-performance, edge-first web application built with **Astro** and deployed on **Cloudflare Pages**. It includes serverless API routes, R2 integration, and D1 database management, making it an ideal choice for scalable web applications. +This repository contains the **Flarekit Web App**, a high-performance, edge-first web application built with **Astro** and deployed on **Cloudflare Pages**. It includes serverless API routes, R2 integration, and D1 database management, making it an ideal choice for scalable web applications. --- @@ -18,7 +18,7 @@ This repository contains the **Astroflare Web App**, a high-performance, edge-fi ## **Overview** -The Astroflare Web App is part of the Astroflare ecosystem. This project leverages: +The Flarekit Web App is part of the Flarekit ecosystem. This project leverages: - **Astro**: For creating fast, SEO-friendly frontends. - **Cloudflare Pages**: For edge-first deployment and serverless APIs. @@ -42,7 +42,7 @@ The Astroflare Web App is part of the Astroflare ecosystem. This project leverag ### **1. Clone the Repository** ```bash -git clone https://github.com/Atyantik/astroflare.git +git clone https://github.com/Atyantik/flarekit.git cd apps/web ``` @@ -116,7 +116,7 @@ src/ ## **Contributing** -We welcome contributions to improve Astroflare. Please follow these steps: +We welcome contributions to improve Flarekit. Please follow these steps: 1. Fork the repository. 2. Create a new branch: @@ -137,7 +137,7 @@ We welcome contributions to improve Astroflare. Please follow these steps: ## **About Atyantik Technologies** -Astroflare is proudly supported and sponsored by **[Atyantik Technologies](https://atyantik.com)**, a leading software development company specializing in scalable web applications, cloud services, and cutting-edge technologies. +Flarekit is proudly supported and sponsored by **[Atyantik Technologies](https://atyantik.com)**, a leading software development company specializing in scalable web applications, cloud services, and cutting-edge technologies.

Atyantik Technologies @@ -149,4 +149,4 @@ Astroflare is proudly supported and sponsored by **[Atyantik Technologies](https - ๐Ÿ’ผ [LinkedIn](https://www.linkedin.com/company/atyantik-technologies/) - ๐Ÿฆ [Twitter](https://twitter.com/atyantik_tech) -Astroflare Web App โ€“ Simplifying Edge-First Development with Cloudflare ๐ŸŒโœจ +Flarekit Web App โ€“ Simplifying Edge-First Development with Cloudflare ๐ŸŒโœจ diff --git a/apps/web/astro.config.mjs b/apps/web/astro.config.mjs index 5bb6785..d332136 100644 --- a/apps/web/astro.config.mjs +++ b/apps/web/astro.config.mjs @@ -18,7 +18,7 @@ export default defineConfig({ }, vite: { optimizeDeps: { - include: ["@services/database"], + include: ["@flarekit/database"], }, }, }); diff --git a/apps/web/eslint.config.js b/apps/web/eslint.config.js index 022268c..c27c0fa 100644 --- a/apps/web/eslint.config.js +++ b/apps/web/eslint.config.js @@ -2,10 +2,13 @@ import globals from "globals"; import eslintPluginAstro from "eslint-plugin-astro"; import astroParser from "astro-eslint-parser"; import tsParser from "@typescript-eslint/parser"; -import rootConfig from "../../eslint.config.js"; +import { + defaultRootConfig, + eslintConfigPrettier, +} from "../../eslint.config.js"; export default [ - ...rootConfig, + ...defaultRootConfig, { settings: { react: { @@ -38,4 +41,5 @@ export default [ // add more generic rule sets here, such as: // js.configs.recommended, ...eslintPluginAstro.configs.recommended, + eslintConfigPrettier, ]; diff --git a/apps/web/package.json b/apps/web/package.json index 89d7850..6cc1e43 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,5 +1,5 @@ { - "name": "@apps/web", + "name": "@flarekit/web", "version": "0.0.1", "type": "module", "scripts": { @@ -12,12 +12,17 @@ "dependencies": { "@astrojs/check": "^0.9.4", "@astrojs/cloudflare": "^12.1.0", - "@services/database": "*", + "@flarekit/database": "*", "astro": "^5.1.1", "typescript": "^5.7.2" }, "devDependencies": { + "@eslint/js": "^9.17.0", "@types/node": "^22.10.2", + "@typescript-eslint/eslint-plugin": "^8.19.0", + "@typescript-eslint/parser": "^8.19.0", + "astro-eslint-parser": "^1.1.0", + "eslint-plugin-astro": "^1.3.1", "prettier": "^3.4.2", "prettier-plugin-astro": "^0.14.1", "tsx": "^4.19.2" diff --git a/apps/web/src/env.d.ts b/apps/web/src/env.d.ts index e2d077a..b9e6a33 100644 --- a/apps/web/src/env.d.ts +++ b/apps/web/src/env.d.ts @@ -1,8 +1,16 @@ -type DrizzleD1Database = import("drizzle-orm/d1").DrizzleD1Database; -type Runtime = import("@astrojs/cloudflare").Runtime; +// global.d.ts or env.d.ts (make sure it's included in tsconfig.json "include") +import { initDBInstance } from "@flarekit/database"; +import type { Runtime } from "@astrojs/cloudflare"; -declare namespace App { - interface Locals extends Runtime { - dbClient: DrizzleD1Database; +declare global { + // or "declare module 'astro' { ... }" if youโ€™re augmenting Astroโ€™s types + namespace App { + interface Locals extends Runtime { + // If initDBInstance is async, you might need Awaited<> + DB: ReturnType; + } } } + +// Required for the file to be treated as a module +export { }; diff --git a/apps/web/src/middleware.ts b/apps/web/src/middleware.ts index 2067683..5ab811e 100644 --- a/apps/web/src/middleware.ts +++ b/apps/web/src/middleware.ts @@ -1,13 +1,12 @@ import { defineMiddleware } from "astro:middleware"; -import { getDBClient } from "@services/database"; +import { initDBInstance } from "@flarekit/database"; export const onRequest = defineMiddleware(async (context, next) => { // intercept data from a request // optionally, modify the properties in `locals` - context.locals.dbClient = await getDBClient( - context, - context.locals.runtime.env.DB, - ); + context.locals.DB = await initDBInstance(context, context.locals.runtime.env); + + context.locals.DB.storage; // return a Response or the result of calling `next()` return next(); diff --git a/apps/web/src/pages/gallery/index.astro b/apps/web/src/pages/gallery/index.astro index 292ecb7..4fb0cc5 100644 --- a/apps/web/src/pages/gallery/index.astro +++ b/apps/web/src/pages/gallery/index.astro @@ -2,19 +2,19 @@ import Layout from "@layouts/Layout.astro"; import "./style.css"; -import { listStorageRecords } from "@services/database"; +const { DB } = Astro.locals; const MAX_FILE_SIZE = 1024 * 1024 * 2; // 2MB const cache = Astro.locals.runtime.env.CACHE; let cachedStorageRecords = await cache.get("storage_records"); if (!cachedStorageRecords) { - const storageRecords = await listStorageRecords(Astro.locals.dbClient); + const storageRecords = await DB.storage.listStorageRecords(); await cache.put("storage_records", JSON.stringify(storageRecords)); cachedStorageRecords = JSON.stringify(storageRecords); } const records = JSON.parse(cachedStorageRecords) as Awaited< - ReturnType + ReturnType >; --- diff --git a/apps/web/src/pages/upload.astro b/apps/web/src/pages/upload.astro index 06e4951..c04df7e 100644 --- a/apps/web/src/pages/upload.astro +++ b/apps/web/src/pages/upload.astro @@ -16,7 +16,6 @@ if (Astro.request.method === "POST") { throw new Error("File size too large"); } const fileData = await handleFile(image, Astro.locals); - console.log(fileData); return Astro.redirect("/gallery"); } } catch { diff --git a/apps/web/src/utils/upload.util.ts b/apps/web/src/utils/upload.util.ts index bdc215b..8606058 100644 --- a/apps/web/src/utils/upload.util.ts +++ b/apps/web/src/utils/upload.util.ts @@ -1,12 +1,8 @@ import type { R2Bucket } from "@cloudflare/workers-types"; import { fileExists, uploadFile, validateFile } from "@utils/r2-storage.util"; import { computeShortHash } from "@utils/hash.util"; -import type { DrizzleD1Database } from "drizzle-orm/d1"; -import { - createStorageRecord, - getStorageRecordFromKey, - type SelectStorageType, -} from "@services/database"; + +type DB = globalThis.App.Locals["DB"]; /** * Generates a unique key for the file using its hash and name. @@ -28,11 +24,7 @@ function generateKey(hashHex: string, fileName: string): string { * @returns The URL of the uploaded image. * @throws Error if any step fails. */ -async function handleUpload( - formFile: File, - storage: R2Bucket, - db: DrizzleD1Database, -): Promise { +async function handleUpload(formFile: File, storage: R2Bucket, db: DB) { // Validate the image const file = validateFile(formFile); @@ -54,19 +46,16 @@ async function handleUpload( } // Check if the DB Entry exists! - let fileFromKey = await getStorageRecordFromKey(key, db); + let fileFromKey = await db.storage.getStorageRecordFromKey(key); if (!fileFromKey) { - await createStorageRecord( - { - key, - originalName: file.name, - size: file.size, - mimeType: file.type, - hash: hashHex, - }, - db, - ); - fileFromKey = await getStorageRecordFromKey(key, db); + await db.storage.createStorageRecord({ + key, + originalName: file.name, + size: file.size, + mimeType: file.type, + hash: hashHex, + }); + fileFromKey = await db.storage.getStorageRecordFromKey(key); } // Construct the CDN URL @@ -92,10 +81,10 @@ export const handleFile = async (file: File, locals: globalThis.App.Locals) => { if (!storage) { throw new Error("You need to add storage binding to the environment."); } - const { dbClient } = locals; + const { DB } = locals; try { // Handle the upload process - const fileData = await handleUpload(file, storage, dbClient); + const fileData = await handleUpload(file, storage, DB); // Empty the cache await cache.delete("storage_records"); // Respond with the image URL diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index fcaa899..c0629f3 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -1,6 +1,12 @@ { "extends": "astro/tsconfigs/strict", - "include": [".astro/types.d.ts", "**/*", "../../scripts", ".prettierrc.mjs"], + "include": [ + ".astro/types.d.ts", + "**/*", + "../../scripts", + ".prettierrc.mjs", + "../../packages/database/dist/types/index.d.ts" + ], "exclude": ["dist"], "compilerOptions": { "lib": ["DOM", "ESNext"], diff --git a/apps/web/wrangler.config.json b/apps/web/wrangler.config.json index 85cdf6a..491d0fc 100644 --- a/apps/web/wrangler.config.json +++ b/apps/web/wrangler.config.json @@ -1,5 +1,5 @@ { "$schema": "../../node_modules/wrangler/config-schema.json", - "name": "astroflare-web", + "name": "flarekit-web", "pages_build_output_dir": "./dist" } diff --git a/apps/worker/.editorconfig b/apps/worker/.editorconfig deleted file mode 100644 index a727df3..0000000 --- a/apps/worker/.editorconfig +++ /dev/null @@ -1,12 +0,0 @@ -# http://editorconfig.org -root = true - -[*] -indent_style = tab -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.yml] -indent_style = space diff --git a/apps/worker/.gitignore b/apps/worker/.gitignore deleted file mode 100644 index 414ec65..0000000 --- a/apps/worker/.gitignore +++ /dev/null @@ -1,176 +0,0 @@ -# Logs - -logs -_.log -npm-debug.log_ -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -.pnpm-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) - -report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json - -# Runtime data - -pids -_.pid -_.seed -\*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover - -lib-cov - -# Coverage directory used by tools like istanbul - -coverage -\*.lcov - -# nyc test coverage - -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) - -.grunt - -# Bower dependency directory (https://bower.io/) - -bower_components - -# node-waf configuration - -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) - -build/Release - -# Dependency directories - -node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) - -web_modules/ - -# TypeScript cache - -\*.tsbuildinfo - -# Optional npm cache directory - -.npm - -# Optional eslint cache - -.eslintcache - -# Optional stylelint cache - -.stylelintcache - -# Microbundle cache - -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history - -.node_repl_history - -# Output of 'npm pack' - -\*.tgz - -# Yarn Integrity file - -.yarn-integrity - -# dotenv environment variable files - -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# parcel-bundler cache (https://parceljs.org/) - -.cache -.parcel-cache - -# Next.js build output - -.next -out - -# Nuxt.js build / generate output - -.nuxt -dist - -# Gatsby files - -.cache/ - -# Comment in the public line in if your project uses Gatsby and not Next.js - -# https://nextjs.org/blog/next-9-1#public-directory-support - -# public - -# vuepress build output - -.vuepress/dist - -# vuepress v2.x temp and cache directory - -.temp -.cache - -# Docusaurus cache and generated files - -.docusaurus - -# Serverless directories - -.serverless/ - -# FuseBox cache - -.fusebox/ - -# DynamoDB Local files - -.dynamodb/ - -# TernJS port file - -.tern-port - -# Stores VSCode versions used for testing VSCode extensions - -.vscode-test - -# yarn v2 - -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.\* - -# wrangler project - -.dev.vars -.wrangler/ - -# Remove wrangler.json as we will generate runtime -wrangler.json -.dev.vars diff --git a/apps/worker/src/index.ts b/apps/worker/src/index.ts deleted file mode 100644 index ec8916c..0000000 --- a/apps/worker/src/index.ts +++ /dev/null @@ -1,63 +0,0 @@ -import type { ExportedHandler } from '@cloudflare/workers-types'; -import { - clearStorageRecords, - getDBClient, - listStorageRecords, -} from '@services/database'; - -export default { - async fetch(request, env, ctx): Promise { - const DB = await getDBClient(this, env.DB); - let storageRecords: Record[] = []; - try { - storageRecords = await listStorageRecords(DB); - } catch { - // Do nothing - } - - const data = { - ctx, - storageRecords, - headers: Object.fromEntries(request.headers.entries()), - env: Object.keys(env), - method: request.method, - url: request.url, - }; - return new Response(JSON.stringify(data, null, 2), { - headers: { 'content-type': 'application/json' }, - }); - }, - - /* istanbul ignore next: Cannot test Queue invocation */ - // async queue( batch: MessageBatch, env: Environment, ctx: ExecutionContext) - async queue(batch): Promise { - let messages = JSON.stringify(batch.messages); - console.log(`Consumed from our queue: ${messages}`); - batch.ackAll(); - }, - - /* istanbul ignore next: Cannot test scheduled invocation */ - // scheduled(event: ScheduledEvent, env: Environment, ctx: ExecutionContext) - async scheduled(event, env, ctx) { - // Pass a promise - ctx.waitUntil( - (async () => { - // Clear the storage every 5th minute - if (event.cron.startsWith('*/5')) { - const DB = await getDBClient(this, env.DB); - const STORAGE = env.STORAGE; - const CACHE = env.CACHE; - // Get all storage Records - const storageRecords = await listStorageRecords(DB); - // Remove each storage record from - for (const record of storageRecords) { - await STORAGE.delete(record.key); - } - await clearStorageRecords(DB); - await CACHE.delete('storage_records'); - } - })(), - ); - }, - // eslint-disable-next-line no-undef -} satisfies ExportedHandler; diff --git a/apps/worker/test/index.spec.ts b/apps/worker/test/index.spec.ts deleted file mode 100644 index 471c5e8..0000000 --- a/apps/worker/test/index.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -// test/index.spec.ts -import { - env, - createExecutionContext, - waitOnExecutionContext, -} from 'cloudflare:test'; -import { describe, it, expect } from 'vitest'; -import worker from '../src/index'; - -// For now, you'll need to do something like this to get a correctly-typed -// `Request` to pass to `worker.fetch()`. -// eslint-disable-next-line no-undef -const IncomingRequest = Request; - -describe('Hello World worker', () => { - it('responds with request data as json', async () => { - const request = new IncomingRequest('http://example.com'); - // Create an empty context to pass to `worker.fetch()`. - const ctx = createExecutionContext(); - // @ts-expect-error known compatibility issues - const response = await worker.fetch(request, env, ctx); - // Wait for all `Promise`s passed to `ctx.waitUntil()` to settle before running test assertions - await waitOnExecutionContext(ctx); - expect(await response.json()).toBeInstanceOf(Object); - }); -}); diff --git a/apps/worker/test/tsconfig.json b/apps/worker/test/tsconfig.json deleted file mode 100644 index 42360ca..0000000 --- a/apps/worker/test/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "types": [ - "@cloudflare/workers-types/experimental", - "@cloudflare/vitest-pool-workers" - ] - }, - "include": ["./**/*.ts", "../worker-configuration.d.ts"], - "exclude": [] -} diff --git a/apps/worker/tsconfig.json b/apps/worker/tsconfig.json deleted file mode 100644 index 003d2bd..0000000 --- a/apps/worker/tsconfig.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ - - /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - "target": "es2021", - /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - "lib": ["es2021"], - /* Specify what JSX code is generated. */ - "jsx": "react-jsx", - - /* Specify what module code is generated. */ - "module": "es2022", - /* Specify how TypeScript looks up a file from a given module specifier. */ - "moduleResolution": "Bundler", - /* Specify type package names to be included without being referenced in a source file. */ - "types": ["@cloudflare/workers-types"], - /* Enable importing .json files */ - "resolveJsonModule": true, - - /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ - "allowJs": true, - /* Enable error reporting in type-checked JavaScript files. */ - "checkJs": false, - - /* Disable emitting files from a compilation. */ - "noEmit": true, - - /* Ensure that each file can be safely transpiled without relying on other imports. */ - "isolatedModules": true, - /* Allow 'import x from y' when a module doesn't have a default export. */ - "allowSyntheticDefaultImports": true, - /* Ensure that casing is correct in imports. */ - "forceConsistentCasingInFileNames": true, - - /* Enable all strict type-checking options. */ - "strict": true, - - /* Skip type checking all .d.ts files. */ - "skipLibCheck": true - }, - "exclude": ["test"], - "include": ["worker-configuration.d.ts", "src/**/*.ts"] -} diff --git a/eslint.config.js b/eslint.config.js index aead0b8..5d7aa5c 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,12 +1,7 @@ -import globals from 'globals'; -import pluginJs from '@eslint/js'; -import tsPlugin from '@typescript-eslint/eslint-plugin'; -import tsParser from '@typescript-eslint/parser'; -import pluginReact from 'eslint-plugin-react'; import eslintConfigPrettier from 'eslint-config-prettier'; /** @type {import('eslint').Linter.Config[]} */ -export default [ +export const defaultRootConfig = [ { ignores: [ '**/dist/**', @@ -20,41 +15,13 @@ export default [ '**/*.d.ts', ], }, - { - settings: { - react: { - version: 'detect', // Automatically detect React version - }, - }, - }, - { - files: ['**/*.{js,mjs,cjs,ts,jsx,tsx}'], - languageOptions: { - globals: { - ...globals.browser, - ...globals.node, - }, - parser: tsParser, - parserOptions: { - ecmaVersion: 2021, - sourceType: 'module', - ecmaFeatures: { - jsx: true, - }, - }, - }, - plugins: { - '@typescript-eslint': tsPlugin, - react: pluginReact, - }, - rules: { - ...pluginJs.configs.recommended.rules, - ...tsPlugin.configs.recommended.rules, - ...pluginReact.configs.recommended.rules, - }, - }, - { - // Always keep eslintConfigPrettier last to disable conflicting rules - ...eslintConfigPrettier, - }, +]; + +export { eslintConfigPrettier }; + +/** @type {import('eslint').Linter.Config[]} */ +export default [ + ...defaultRootConfig, + // Always keep eslintConfigPrettier last to disable conflicting rules + eslintConfigPrettier, ]; diff --git a/package-lock.json b/package-lock.json index 4efeabe..ba62411 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,23 +1,20 @@ { - "name": "cloudflare-astro-apps", + "name": "flarekit", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "cloudflare-astro-apps", + "name": "flarekit", "workspaces": [ "apps/*", "packages/*" ], "devDependencies": { - "@eslint/js": "^9.17.0", "@types/eslint-config-prettier": "^6.11.3", - "@typescript-eslint/eslint-plugin": "^8.18.2", - "@typescript-eslint/parser": "^8.18.2", + "@types/node": "^22.10.3", "@vitest/coverage-istanbul": "^2.1.8", "eslint": "^9.17.0", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-astro": "^1.3.1", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-react": "^7.37.3", @@ -27,7 +24,6 @@ "tsc-alias": "^1.8.10", "turbo": "^2.3.3", "typescript": "^5.5.4", - "typescript-eslint": "^8.18.2", "vite-tsconfig-paths": "^5.1.4", "vitest": "^2.1.8", "wrangler": "^3.99.0" @@ -36,37 +32,42 @@ "node": ">=18" } }, + "apps/api": { + "name": "@flarekit/api", + "dependencies": { + "@flarekit/database": "*", + "hono": "^4.6.15" + }, + "devDependencies": { + "@cloudflare/vitest-pool-workers": "^0.5.40", + "@cloudflare/workers-types": "^4.20241230.0", + "typescript": "^5.7.2", + "vitest": "^2.1.8", + "wrangler": "^3.99.0" + } + }, "apps/web": { - "name": "@apps/web", + "name": "@flarekit/web", "version": "0.0.1", "dependencies": { "@astrojs/check": "^0.9.4", "@astrojs/cloudflare": "^12.1.0", - "@services/database": "*", + "@flarekit/database": "*", "astro": "^5.1.1", "typescript": "^5.7.2" }, "devDependencies": { + "@eslint/js": "^9.17.0", "@types/node": "^22.10.2", + "@typescript-eslint/eslint-plugin": "^8.19.0", + "@typescript-eslint/parser": "^8.19.0", + "astro-eslint-parser": "^1.1.0", + "eslint-plugin-astro": "^1.3.1", "prettier": "^3.4.2", "prettier-plugin-astro": "^0.14.1", "tsx": "^4.19.2" } }, - "apps/worker": { - "name": "@apps/worker", - "version": "1.0.0", - "dependencies": { - "@services/database": "*" - }, - "devDependencies": { - "@cloudflare/vitest-pool-workers": "^0.5.2", - "@cloudflare/workers-types": "^4.20241224.0", - "typescript": "^5.5.2", - "vitest": "2.1.8", - "wrangler": "^3.99.0" - } - }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -81,14 +82,6 @@ "node": ">=6.0.0" } }, - "node_modules/@apps/web": { - "resolved": "apps/web", - "link": true - }, - "node_modules/@apps/worker": { - "resolved": "apps/worker", - "link": true - }, "node_modules/@astrojs/check": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/@astrojs/check/-/check-0.9.4.tgz", @@ -817,16 +810,6 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/generator": { "version": "7.26.3", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", @@ -861,16 +844,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/helper-module-imports": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", @@ -1463,6 +1436,19 @@ "@esbuild/win32-x64": "0.17.19" } }, + "node_modules/@cloudflare/vitest-pool-workers/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@cloudflare/workerd-darwin-64": { "version": "1.20241218.0", "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20241218.0.tgz", @@ -1544,9 +1530,9 @@ } }, "node_modules/@cloudflare/workers-types": { - "version": "4.20241224.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20241224.0.tgz", - "integrity": "sha512-1ZmFc8qqM7S/HUGmLplc4P8n8DoMqiJmc47r9Lr7VbuaotoqCXVljz09w1V1mc4K3pbFPgvqSy4XYStZ08HrlQ==", + "version": "4.20241230.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20241230.0.tgz", + "integrity": "sha512-dtLD4jY35Lb750cCVyO1i/eIfdZJg2Z0i+B1RYX6BVeRPlgaHx/H18ImKAkYmy0g09Ow8R2jZy3hIxMgXun0WQ==", "license": "MIT OR Apache-2.0" }, "node_modules/@cspotcode/source-map-support": { @@ -2520,6 +2506,19 @@ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@eslint-community/regexpp": { "version": "4.12.1", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", @@ -2545,30 +2544,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@eslint/core": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz", @@ -2606,17 +2581,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -2630,19 +2594,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@eslint/js": { "version": "9.17.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", @@ -2685,6 +2636,18 @@ "node": ">=14" } }, + "node_modules/@flarekit/api": { + "resolved": "apps/api", + "link": true + }, + "node_modules/@flarekit/database": { + "resolved": "packages/database", + "link": true + }, + "node_modules/@flarekit/web": { + "resolved": "apps/web", + "link": true + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -3113,13 +3076,13 @@ } }, "node_modules/@inox-tools/astro-when": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@inox-tools/astro-when/-/astro-when-1.0.1.tgz", - "integrity": "sha512-x/62MFEfzSMYaQGIoxg5k10YL5X9LYWEmFPtew/Z3AOKhWrLx+LHnqimRkbfV4JVMDj+F0ZP5a+xRRjHbZ6Maw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inox-tools/astro-when/-/astro-when-1.0.2.tgz", + "integrity": "sha512-/r7asjHWVIkDsu0m92rGF+Hg5Gj3xtpJXonffzhIB7KtA3JYwCLgdA1hvroRLhq7nHxlYZXXE9WxnRtWbeJAGA==", "license": "MIT", "dependencies": { - "astro-integration-kit": "~0.17.0", - "debug": "^4.3.7" + "astro-integration-kit": "~0.18.0", + "debug": "^4.4.0" }, "peerDependencies": { "astro": "^5" @@ -3761,49 +3724,63 @@ "win32" ] }, - "node_modules/@services/database": { - "resolved": "packages/database", - "link": true - }, "node_modules/@shikijs/core": { - "version": "1.24.4", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.24.4.tgz", - "integrity": "sha512-jjLsld+xEEGYlxAXDyGwWsKJ1sw5Pc1pnp4ai2ORpjx2UX08YYTC0NNqQYO1PaghYaR+PvgMOGuvzw2he9sk0Q==", + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.25.1.tgz", + "integrity": "sha512-0j5k3ZkLTQViOuNzPVyWGoW1zgH3kiFdUT/JOCkTm7TU74mz+dF+NID+YoiCBzHQxgsDpcGYPjKDJRcuVLSt4A==", "license": "MIT", "dependencies": { - "@shikijs/engine-javascript": "1.24.4", - "@shikijs/engine-oniguruma": "1.24.4", - "@shikijs/types": "1.24.4", + "@shikijs/engine-javascript": "1.25.1", + "@shikijs/engine-oniguruma": "1.25.1", + "@shikijs/types": "1.25.1", "@shikijs/vscode-textmate": "^9.3.1", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.4" } }, "node_modules/@shikijs/engine-javascript": { - "version": "1.24.4", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.24.4.tgz", - "integrity": "sha512-TClaQOLvo9WEMJv6GoUsykQ6QdynuKszuORFWCke8qvi6PeLm7FcD9+7y45UenysxEWYpDL5KJaVXTngTE+2BA==", + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.25.1.tgz", + "integrity": "sha512-zQ7UWKnRCfD/Q1M+XOSyjsbhpE0qv8LUnmn82HYCeOsgAHgUZGEDIQ63bbuK3kU5sQg+2CtI+dPfOqD/mjSY9w==", "license": "MIT", "dependencies": { - "@shikijs/types": "1.24.4", + "@shikijs/types": "1.25.1", "@shikijs/vscode-textmate": "^9.3.1", - "oniguruma-to-es": "0.8.1" + "oniguruma-to-es": "0.10.0" } }, "node_modules/@shikijs/engine-oniguruma": { - "version": "1.24.4", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.24.4.tgz", - "integrity": "sha512-Do2ry6flp2HWdvpj2XOwwa0ljZBRy15HKZITzPcNIBOGSeprnA8gOooA/bLsSPuy8aJBa+Q/r34dMmC3KNL/zw==", + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.25.1.tgz", + "integrity": "sha512-iKPMh3H+0USHtWfZ1irfMTH6tGmIUFSnqt3E2K8BgI1VEsqiPh0RYkG2WTwzNiM1/WHN4FzYx/nrKR7PDHiRyw==", "license": "MIT", "dependencies": { - "@shikijs/types": "1.24.4", + "@shikijs/types": "1.25.1", "@shikijs/vscode-textmate": "^9.3.1" } }, + "node_modules/@shikijs/langs": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-1.25.1.tgz", + "integrity": "sha512-hdYjq9aRJplAzGe2qF51PR9IDgEoyGb4IkXvr3Ts6lEdg4Z8M/kdknKRo2EIuv3IR/aKkJXTlBQRM+wr3t20Ew==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "1.25.1" + } + }, + "node_modules/@shikijs/themes": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-1.25.1.tgz", + "integrity": "sha512-JO0lDn4LgGqg5QKvgich5ScUmC2okK+LxM9a3iLUH7YMeI2c8UGXThuJv6sZduS7pdJbYQHPrvWq9t/V4GhpbQ==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "1.25.1" + } + }, "node_modules/@shikijs/types": { - "version": "1.24.4", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.24.4.tgz", - "integrity": "sha512-0r0XU7Eaow0PuDxuWC1bVqmWCgm3XqizIaT7SM42K03vc69LGooT0U8ccSR44xP/hGlNx4FKhtYpV+BU6aaKAA==", + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.25.1.tgz", + "integrity": "sha512-dceqFUoO95eY4tpOj3OGq8wE8EgJ4ey6Me1HQEu5UbwIYszFndEll/bjlB8Kp9wl4fx3uM7n4+y9XCYuDBmcXA==", "license": "MIT", "dependencies": { "@shikijs/vscode-textmate": "^9.3.1", @@ -3885,9 +3862,9 @@ } }, "node_modules/@types/node": { - "version": "22.10.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", - "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", + "version": "22.10.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.3.tgz", + "integrity": "sha512-DifAyw4BkrufCILvD3ucnuN8eydUfc/C1GlyrnI+LK6543w5/L3VeVgf05o3B4fqSXP1dKYLOZsKfutpxPzZrw==", "license": "MIT", "dependencies": { "undici-types": "~6.20.0" @@ -3916,17 +3893,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.18.2.tgz", - "integrity": "sha512-adig4SzPLjeQ0Tm+jvsozSGiCliI2ajeURDGHjZ2llnA+A67HihCQ+a3amtPhUakd1GlwHxSRvzOZktbEvhPPg==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.19.0.tgz", + "integrity": "sha512-NggSaEZCdSrFddbctrVjkVZvFC6KGfKfNK0CU7mNK/iKHGKbzT4Wmgm08dKpcZECBu9f5FypndoMyRHkdqfT1Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.18.2", - "@typescript-eslint/type-utils": "8.18.2", - "@typescript-eslint/utils": "8.18.2", - "@typescript-eslint/visitor-keys": "8.18.2", + "@typescript-eslint/scope-manager": "8.19.0", + "@typescript-eslint/type-utils": "8.19.0", + "@typescript-eslint/utils": "8.19.0", + "@typescript-eslint/visitor-keys": "8.19.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -3946,16 +3923,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.18.2.tgz", - "integrity": "sha512-y7tcq4StgxQD4mDr9+Jb26dZ+HTZ/SkfqpXSiqeUXZHxOUyjWDKsmwKhJ0/tApR08DgOhrFAoAhyB80/p3ViuA==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.19.0.tgz", + "integrity": "sha512-6M8taKyOETY1TKHp0x8ndycipTVgmp4xtg5QpEZzXxDhNvvHOJi5rLRkLr8SK3jTgD5l4fTlvBiRdfsuWydxBw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.18.2", - "@typescript-eslint/types": "8.18.2", - "@typescript-eslint/typescript-estree": "8.18.2", - "@typescript-eslint/visitor-keys": "8.18.2", + "@typescript-eslint/scope-manager": "8.19.0", + "@typescript-eslint/types": "8.19.0", + "@typescript-eslint/typescript-estree": "8.19.0", + "@typescript-eslint/visitor-keys": "8.19.0", "debug": "^4.3.4" }, "engines": { @@ -3971,14 +3948,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.18.2.tgz", - "integrity": "sha512-YJFSfbd0CJjy14r/EvWapYgV4R5CHzptssoag2M7y3Ra7XNta6GPAJPPP5KGB9j14viYXyrzRO5GkX7CRfo8/g==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.19.0.tgz", + "integrity": "sha512-hkoJiKQS3GQ13TSMEiuNmSCvhz7ujyqD1x3ShbaETATHrck+9RaDdUbt+osXaUuns9OFwrDTTrjtwsU8gJyyRA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.18.2", - "@typescript-eslint/visitor-keys": "8.18.2" + "@typescript-eslint/types": "8.19.0", + "@typescript-eslint/visitor-keys": "8.19.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3989,14 +3966,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.18.2.tgz", - "integrity": "sha512-AB/Wr1Lz31bzHfGm/jgbFR0VB0SML/hd2P1yxzKDM48YmP7vbyJNHRExUE/wZsQj2wUCvbWH8poNHFuxLqCTnA==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.19.0.tgz", + "integrity": "sha512-TZs0I0OSbd5Aza4qAMpp1cdCYVnER94IziudE3JU328YUHgWu9gwiwhag+fuLeJ2LkWLXI+F/182TbG+JaBdTg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.18.2", - "@typescript-eslint/utils": "8.18.2", + "@typescript-eslint/typescript-estree": "8.19.0", + "@typescript-eslint/utils": "8.19.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -4013,9 +3990,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.18.2.tgz", - "integrity": "sha512-Z/zblEPp8cIvmEn6+tPDIHUbRu/0z5lqZ+NvolL5SvXWT5rQy7+Nch83M0++XzO0XrWRFWECgOAyE8bsJTl1GQ==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.0.tgz", + "integrity": "sha512-8XQ4Ss7G9WX8oaYvD4OOLCjIQYgRQxO+qCiR2V2s2GxI9AUpo7riNwo6jDhKtTcaJjT8PY54j2Yb33kWtSJsmA==", "dev": true, "license": "MIT", "engines": { @@ -4027,14 +4004,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.18.2.tgz", - "integrity": "sha512-WXAVt595HjpmlfH4crSdM/1bcsqh+1weFRWIa9XMTx/XHZ9TCKMcr725tLYqWOgzKdeDrqVHxFotrvWcEsk2Tg==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.0.tgz", + "integrity": "sha512-WW9PpDaLIFW9LCbucMSdYUuGeFUz1OkWYS/5fwZwTA+l2RwlWFdJvReQqMUMBw4yJWJOfqd7An9uwut2Oj8sLw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.18.2", - "@typescript-eslint/visitor-keys": "8.18.2", + "@typescript-eslint/types": "8.19.0", + "@typescript-eslint/visitor-keys": "8.19.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -4053,17 +4030,56 @@ "typescript": ">=4.8.4 <5.8.0" } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/utils": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.18.2.tgz", - "integrity": "sha512-Cr4A0H7DtVIPkauj4sTSXVl+VBWewE9/o40KcF3TV9aqDEOWoXF3/+oRXNby3DYzZeCATvbdksYsGZzplwnK/Q==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.19.0.tgz", + "integrity": "sha512-PTBG+0oEMPH9jCZlfg07LCB2nYI0I317yyvXGfxnvGvw4SHIOuRnQ3kadyyXY6tGdChusIHIbM5zfIbp4M6tCg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.18.2", - "@typescript-eslint/types": "8.18.2", - "@typescript-eslint/typescript-estree": "8.18.2" + "@typescript-eslint/scope-manager": "8.19.0", + "@typescript-eslint/types": "8.19.0", + "@typescript-eslint/typescript-estree": "8.19.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4078,13 +4094,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.18.2.tgz", - "integrity": "sha512-zORcwn4C3trOWiCqFQP1x6G3xTRyZ1LYydnj51cRnJ6hxBlr/cKPckk+PKPUw/fXmvfKTcw7bwY3w9izgx5jZw==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.0.tgz", + "integrity": "sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.18.2", + "@typescript-eslint/types": "8.19.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -4095,19 +4111,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/@ungap/structured-clone": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz", @@ -4718,9 +4721,9 @@ "license": "MIT" }, "node_modules/astro": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/astro/-/astro-5.1.1.tgz", - "integrity": "sha512-prpWC2PRs4P3FKQg6gZaU+VNMqbZi5pDvORGB2nrjfRjkrvF6/l4BqhvkJ6YQ0Ohm5rIMVz8ljgaRI77mLHbwg==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/astro/-/astro-5.1.2.tgz", + "integrity": "sha512-+U5lXPEJZ6cQx0botGbPhzN6XGWRgDtXgy/RUkpTmUj18LW6pbzYo0O0k3hFWOazlI039bZ+4P2e/oSNlKzm0Q==", "license": "MIT", "dependencies": { "@astrojs/compiler": "^2.10.3", @@ -4823,30 +4826,30 @@ "url": "https://github.com/sponsors/ota-meshi" } }, - "node_modules/astro-eslint-parser/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "node_modules/astro-eslint-parser/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, - "funding": { - "url": "https://opencollective.com/eslint" + "engines": { + "node": ">=10" } }, "node_modules/astro-integration-kit": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/astro-integration-kit/-/astro-integration-kit-0.17.0.tgz", - "integrity": "sha512-fe31CCKmrGYn/kkBd1J4b7P02gEdMdEIFBz14zdAud+YAmJeLtZD6wmrCz3LDau+lE1oM1hQnvmZXVSM/YveKw==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/astro-integration-kit/-/astro-integration-kit-0.18.0.tgz", + "integrity": "sha512-Z0QW5IQjosuKQDEGYYkvUX6EhEtrmE4/oViqWz23QveV8U7AuyFsTdg00WRNPevWZl/5a4lLUeDpv4bCRynRRg==", "license": "MIT", "dependencies": { "pathe": "^1.1.2", "recast": "^0.23.7" }, "peerDependencies": { - "astro": "^4.12.0 || ^5.0.0-beta" + "astro": "^4.12.0 || ^5.0.0" } }, "node_modules/astro/node_modules/@astrojs/internal-helpers": { @@ -5250,6 +5253,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/astro/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/astro/node_modules/vite": { "version": "6.0.6", "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.6.tgz", @@ -5513,13 +5528,14 @@ } }, "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/braces": { @@ -7202,15 +7218,16 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.4", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -7392,6 +7409,19 @@ "eslint": ">=6.0.0" } }, + "node_modules/eslint-compat-utils/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/eslint-config-prettier": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", @@ -7461,30 +7491,6 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/eslint-plugin-prettier": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", @@ -7549,40 +7555,6 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, - "node_modules/eslint-plugin-react/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint-plugin-react/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/eslint-scope": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", @@ -7601,30 +7573,6 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", @@ -7637,19 +7585,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/espree": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", @@ -7668,19 +7603,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -8108,6 +8030,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.0.tgz", + "integrity": "sha512-TtLgOcKaF1nMP2ijJnITkE4nRhbpshHhmzKiuhmSniiwWzovoqwqQ8rNuhf0mXJOqIY5iU+QkUe0CkJYrLsG9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-source": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/get-source/-/get-source-2.0.12.tgz", @@ -8208,6 +8144,32 @@ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "license": "BSD-2-Clause" }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/globals": { "version": "15.14.0", "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", @@ -8598,6 +8560,15 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hono": { + "version": "4.6.15", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.6.15.tgz", + "integrity": "sha512-OiQwvAOAaI2JrABBH69z5rsctHDzFzIKJge0nYXgtzGJ0KftwLWcBXm1upJC23/omNRtnqM0gjRMbtXshPdqhQ==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -9248,6 +9219,19 @@ "node": ">=10" } }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/istanbul-lib-report": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", @@ -9293,17 +9277,17 @@ } }, "node_modules/iterator.prototype": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.4.tgz", - "integrity": "sha512-x4WH0BWmrMmg4oHHl+duwubhrvczGlyuGAZu3nvrf0UXOfPu8IhZObFEr7DE/iv01YgVZrsOiRcqw2srkKEDIA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", "dev": true, "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", "has-symbols": "^1.1.0", - "reflect.getprototypeof": "^1.0.8", "set-function-name": "^2.0.2" }, "engines": { @@ -9753,6 +9737,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/markdown-table": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", @@ -10667,19 +10664,16 @@ } }, "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "*" } }, "node_modules/minipass": { @@ -10984,14 +10978,14 @@ } }, "node_modules/oniguruma-to-es": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-0.8.1.tgz", - "integrity": "sha512-dekySTEvCxCj0IgKcA2uUCO/e4ArsqpucDPcX26w9ajx+DvMWLc5eZeJaRQkd7oC/+rwif5gnT900tA34uN9Zw==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-0.10.0.tgz", + "integrity": "sha512-zapyOUOCJxt+xhiNRPPMtfJkHGsZ98HHB9qJEkdT8BGytO/+kpe4m1Ngf0MzbzTmhacn11w9yGeDP6tzDhnCdg==", "license": "MIT", "dependencies": { "emoji-regex-xs": "^1.0.0", - "regex": "^5.0.2", - "regex-recursion": "^5.0.0" + "regex": "^5.1.1", + "regex-recursion": "^5.1.1" } }, "node_modules/optionator": { @@ -11079,9 +11073,9 @@ } }, "node_modules/p-timeout": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.3.tgz", - "integrity": "sha512-UJUyfKbwvr/uZSV6btANfb+0t/mOhKV/KXcCUTp8FcQI+v/0d+wXqH4htrW0E4rR6WiEO/EPvUFiV9D5OI4vlw==", + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.4.tgz", + "integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==", "license": "MIT", "engines": { "node": ">=14.16" @@ -12193,15 +12187,13 @@ } }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" } }, "node_modules/set-function-length": { @@ -12278,6 +12270,19 @@ "@img/sharp-win32-x64": "0.33.5" } }, + "node_modules/sharp/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -12302,15 +12307,17 @@ } }, "node_modules/shiki": { - "version": "1.24.4", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.24.4.tgz", - "integrity": "sha512-aVGSFAOAr1v26Hh/+GBIsRVDWJ583XYV7CuNURKRWh9gpGv4OdbisZGq96B9arMYTZhTQkmRF5BrShOSTvNqhw==", + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.25.1.tgz", + "integrity": "sha512-/1boRvNYwRW3GLG9Y6dXdnZ/Ha+J5T/5y3hV7TGQUcDSBM185D3FCbXlz2eTGNKG2iWCbWqo+P0yhGKZ4/CUrw==", "license": "MIT", "dependencies": { - "@shikijs/core": "1.24.4", - "@shikijs/engine-javascript": "1.24.4", - "@shikijs/engine-oniguruma": "1.24.4", - "@shikijs/types": "1.24.4", + "@shikijs/core": "1.25.1", + "@shikijs/engine-javascript": "1.25.1", + "@shikijs/engine-oniguruma": "1.25.1", + "@shikijs/langs": "1.25.1", + "@shikijs/themes": "1.25.1", + "@shikijs/types": "1.25.1", "@shikijs/vscode-textmate": "^9.3.1", "@types/hast": "^3.0.4" } @@ -12910,6 +12917,32 @@ "node": ">=18" } }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/tiny-glob": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", @@ -13769,27 +13802,16 @@ "semver": "^7.3.8" } }, - "node_modules/typescript-eslint": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.18.2.tgz", - "integrity": "sha512-KuXezG6jHkvC3MvizeXgupZzaG5wjhU3yE8E7e6viOvAvD9xAWYp8/vy0WULTGe9DYDWcQu7aW03YIV3mSitrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.18.2", - "@typescript-eslint/parser": "8.18.2", - "@typescript-eslint/utils": "8.18.2" + "node_modules/typescript-auto-import-cache/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "node": ">=10" } }, "node_modules/ufo": { @@ -14307,9 +14329,9 @@ } }, "node_modules/vitefu": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.0.4.tgz", - "integrity": "sha512-y6zEE3PQf6uu/Mt6DTJ9ih+kyJLr4XcSgHR2zUkM8SWDhuixEJxfJ6CZGMHh1Ec3vPLoEA0IHU5oWzVqw8ulow==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.0.5.tgz", + "integrity": "sha512-h4Vflt9gxODPFNGPwp4zAMZRpZR7eslzwH2c5hn5kNZ5rhnKyRJ50U+yGCdc2IRaBs8O4haIgLNGrV5CrpMsCA==", "license": "MIT", "workspaces": [ "tests/deps/*", @@ -14508,6 +14530,18 @@ } } }, + "node_modules/volar-service-typescript/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/volar-service-yaml": { "version": "0.0.62", "resolved": "https://registry.npmjs.org/volar-service-yaml/-/volar-service-yaml-0.0.62.tgz", @@ -15742,7 +15776,7 @@ } }, "packages/database": { - "name": "@services/database", + "name": "@flarekit/database", "version": "1.0.0", "dependencies": { "dotenv": "^16.4.7", @@ -15750,15 +15784,20 @@ "uuid": "^11.0.3" }, "devDependencies": { + "@cloudflare/vitest-pool-workers": "^0.5.40", + "@cloudflare/workers-types": "^4.20241230.0", "@rollup/plugin-commonjs": "^28.0.2", "@rollup/plugin-node-resolve": "^16.0.0", "@rollup/plugin-typescript": "^12.1.2", "@vitest/coverage-istanbul": "^2.1.8", "drizzle-kit": "^0.30.1", + "eslint": "^9.17.0", "miniflare": "^3.20241218.0", "rollup": "^4.29.1", "typescript": "^5.7.2", - "vitest": "^2.1.8" + "vite-tsconfig-paths": "^5.1.4", + "vitest": "^2.1.8", + "wrangler": "^3.99.0" } } } diff --git a/package.json b/package.json index d3bb522..78a7a3c 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "cloudflare-astro-apps", + "name": "flarekit", "private": true, "type": "module", "scripts": { @@ -8,21 +8,18 @@ "deploy:web": "turbo deploy:web", "migrate:d1:local": "echo 'Y' | wrangler d1 migrations apply $(node scripts/parse-d1.js) --local", "migrate:d1:production": "echo 'Y' | wrangler d1 migrations apply $(node scripts/parse-d1.js) --remote", - "test": "npm run setup && turbo run @services/database#build && CI=true vitest run", + "test": "npm run setup && turbo run @flarekit/database#build && CI=true vitest run", "test:coverage": "npm run setup && CI=true vitest --run --coverage", "prepare": "npm exec husky || echo 'Husky not found, skipping...'", "lint": "eslint .", "format": "prettier --write ." }, "devDependencies": { - "@eslint/js": "^9.17.0", "@types/eslint-config-prettier": "^6.11.3", - "@typescript-eslint/eslint-plugin": "^8.18.2", - "@typescript-eslint/parser": "^8.18.2", + "@types/node": "^22.10.3", "@vitest/coverage-istanbul": "^2.1.8", "eslint": "^9.17.0", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-astro": "^1.3.1", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-react": "^7.37.3", @@ -32,7 +29,6 @@ "tsc-alias": "^1.8.10", "turbo": "^2.3.3", "typescript": "^5.5.4", - "typescript-eslint": "^8.18.2", "vite-tsconfig-paths": "^5.1.4", "vitest": "^2.1.8", "wrangler": "^3.99.0" diff --git a/packages/database/.gitignore b/packages/database/.gitignore new file mode 100644 index 0000000..761af54 --- /dev/null +++ b/packages/database/.gitignore @@ -0,0 +1,34 @@ +# prod +dist/ + +# dev +.yarn/ +!.yarn/releases +.vscode/* +!.vscode/launch.json +!.vscode/*.code-snippets +.idea/workspace.xml +.idea/usage.statistics.xml +.idea/shelf + +# deps +node_modules/ +.wrangler + +# env +.env +.env.production +.dev.vars +wrangler.json + +# logs +logs/ +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +# misc +.DS_Store diff --git a/packages/database/eslint.config.js b/packages/database/eslint.config.js new file mode 100644 index 0000000..71ba4b8 --- /dev/null +++ b/packages/database/eslint.config.js @@ -0,0 +1,3 @@ +import rootConfig from '../../eslint.config.js'; + +export default [...rootConfig]; diff --git a/packages/database/package.json b/packages/database/package.json index e007c94..584c29d 100644 --- a/packages/database/package.json +++ b/packages/database/package.json @@ -1,5 +1,5 @@ { - "name": "@services/database", + "name": "@flarekit/database", "version": "1.0.0", "description": "DB Schema and Services", "type": "module", @@ -11,20 +11,16 @@ "import": "./dist/bundle.esm.js", "require": "./dist/bundle.cjs.js", "types": "./dist/types/index.d.ts" - }, - "./test-utils": { - "import": "./dist/__tests__/scripts/test-utils.esm.js", - "require": "./dist/__tests__/scripts/test-utils.cjs.js", - "types": "./dist/__tests__/scripts/types/__tests__/scripts/utils.d.ts" } }, "scripts": { "clean": "rm -rf dist", - "dev": "rollup --config --watch", + "dev": "npm run setup && rollup --config --watch", "build": "npm run build:distribution && npm run build:migrations", - "build:migrations": "drizzle-kit generate", - "build:distribution": "npm run clean && rollup --config", - "test": "CI=true vitest run --coverage" + "build:migrations": "npm run setup && drizzle-kit generate", + "build:distribution": "npm run setup && npm run clean && rollup --config", + "test": "npm run setup && CI=true vitest --run --coverage", + "setup": "node ../../scripts/generate-wrangler.json.js && wrangler types" }, "dependencies": { "dotenv": "^16.4.7", @@ -32,14 +28,19 @@ "uuid": "^11.0.3" }, "devDependencies": { + "@cloudflare/vitest-pool-workers": "^0.5.40", + "@cloudflare/workers-types": "^4.20241230.0", "@rollup/plugin-commonjs": "^28.0.2", "@rollup/plugin-node-resolve": "^16.0.0", "@rollup/plugin-typescript": "^12.1.2", "@vitest/coverage-istanbul": "^2.1.8", "drizzle-kit": "^0.30.1", + "eslint": "^9.17.0", "miniflare": "^3.20241218.0", "rollup": "^4.29.1", "typescript": "^5.7.2", - "vitest": "^2.1.8" + "vite-tsconfig-paths": "^5.1.4", + "vitest": "^2.1.8", + "wrangler": "^3.99.0" } } diff --git a/packages/database/rollup.config.js b/packages/database/rollup.config.js index 850e164..81721dc 100644 --- a/packages/database/rollup.config.js +++ b/packages/database/rollup.config.js @@ -24,27 +24,4 @@ export default [ typescript(), ], }, - { - input: 'src/__tests__/scripts/utils.ts', // Test Utils Entry point - output: [ - { - file: 'dist/__tests__/scripts/test-utils.cjs.js', - format: 'cjs', // CommonJS format - }, - { - file: 'dist/__tests__/scripts/test-utils.esm.js', - format: 'esm', // ES Module format - }, - ], - plugins: [ - // Resolves node_modules imports - resolve(), - // Converts CommonJS modules to ES6 - commonjs(), - // Compiles TypeScript - typescript({ - tsconfig: './tsconfig.test.json', - }), - ], - }, ]; diff --git a/packages/database/src/@types/database.type.ts b/packages/database/src/@types/database.type.ts deleted file mode 100644 index 0b0a23d..0000000 --- a/packages/database/src/@types/database.type.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { DrizzleD1Database } from 'drizzle-orm/d1'; - -export type DB = Omit; diff --git a/packages/database/src/__tests__/client.e2e.test.ts b/packages/database/src/__tests__/client.e2e.test.ts deleted file mode 100644 index 7e0a01f..0000000 --- a/packages/database/src/__tests__/client.e2e.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { describe, it, expect, beforeEach } from 'vitest'; -import { getTestDatabase } from './scripts/global-setup'; -import { getDBClient } from '../client'; - -// eslint-disable-next-line -let db: any; // The actual D1 database instance - -beforeEach(() => { - // Get the test database from global setup - db = getTestDatabase(); -}); - -describe('getDBClient', () => { - it('should return a valid drizzle instance', async () => { - const reference = {}; // A mock reference object - - // Call the function to get the drizzle instance - const drizzleInstance = await getDBClient(reference, db); - - // Verify that drizzleInstance is a valid object - expect(drizzleInstance).toBeDefined(); - expect(typeof drizzleInstance).toBe('object'); - }); - - it('should reuse the same drizzle instance for the same reference', async () => { - const reference = {}; // A mock reference object - - // Get the drizzle instance for the first time - const drizzleInstance1 = await getDBClient(reference, db); - - // Get the drizzle instance again - const drizzleInstance2 = await getDBClient(reference, db); - - // Verify that both instances are the same - expect(drizzleInstance1).toBe(drizzleInstance2); - }); - - it('should create a new drizzle instance for a different reference', async () => { - const reference1 = {}; // First reference - const reference2 = {}; // Second reference - - // Get drizzle instances for two different references - const drizzleInstance1 = await getDBClient(reference1, db); - const drizzleInstance2 = await getDBClient(reference2, db); - - // Verify that the instances are different - expect(drizzleInstance1).not.toBe(drizzleInstance2); - }); -}); diff --git a/packages/database/src/__tests__/schema/storage.schema.e2e.test.ts b/packages/database/src/__tests__/schema/storage.schema.e2e.test.ts deleted file mode 100644 index 1864374..0000000 --- a/packages/database/src/__tests__/schema/storage.schema.e2e.test.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { describe, it, expect, beforeEach } from 'vitest'; -import { AnyD1Database, drizzle } from 'drizzle-orm/d1'; -import { getTestDatabase } from '../scripts/global-setup'; -import { storageSchema } from '../../schema/storage.schema'; -import { eq, sql } from 'drizzle-orm'; - -let db: AnyD1Database; - -beforeEach(() => { - // Initialize the Drizzle instance with the test database - db = drizzle(getTestDatabase()); -}); - -describe('Storage Schema Tests with Drizzle Instance', () => { - it('should insert and retrieve a record', async () => { - const record = { - id: 'test-id', - key: 'file-key', - originalName: 'test-file.txt', - size: 1024, - mimeType: 'text/plain', - hash: 'hash-value', - }; - - // Insert the record using Drizzle ORM - await db.insert(storageSchema).values(record); - - // Retrieve the record - const [retrievedRecord] = await db - .select() - .from(storageSchema) - .where(eq(storageSchema.key, 'file-key')); - - // Validate the retrieved record - expect(retrievedRecord).toMatchObject({ - ...record, - createdAt: expect.any(String), // Auto-generated timestamp - updatedAt: null, - deletedAt: null, - }); - }); - - it('should enforce unique constraint on the key column', async () => { - const record = { - id: 'unique-id-1', - key: 'unique-key', - originalName: 'unique-file.txt', - size: 2048, - mimeType: 'application/json', - hash: 'unique-hash', - }; - - // Insert a record - await db.insert(storageSchema).values(record); - - // Attempt to insert a duplicate record with the same key - await expect( - db.insert(storageSchema).values({ - ...record, - id: 'unique-id-2', // New ID but same key - }), - ).rejects.toThrow(/UNIQUE constraint failed/); - }); - - it('should enforce NOT NULL constraints', async () => { - const invalidRecord = { - id: 'invalid-id', - key: null, // Null value violates the NOT NULL constraint - originalName: 'null-file.txt', - size: 512, - mimeType: 'application/json', - hash: 'null-hash', - }; - - // Attempt to insert an invalid record - await expect( - db.insert(storageSchema).values(invalidRecord), - ).rejects.toThrow(/NOT NULL constraint failed/); - }); - - it('should use the default value for createdAt', async () => { - const record = { - id: 'default-createdAt-id', - key: 'default-createdAt-key', - originalName: 'default-file.txt', - size: 1024, - mimeType: 'text/plain', - hash: 'default-hash', - }; - - // Insert the record without specifying createdAt - await db.insert(storageSchema).values(record); - }); - - it("should update a record's updatedAt column", async () => { - const record = { - id: 'update-id', - key: 'update-key', - originalName: 'update-file.txt', - size: 512, - mimeType: 'application/pdf', - hash: 'update-hash', - }; - - // Insert the record - await db.insert(storageSchema).values(record); - - // Update the record using Drizzle ORM - const updatedAt = new Date().toISOString(); - await db - .update(storageSchema) - .set({ updatedAt }) - .where(eq(storageSchema.key, 'update-key')); - - // Retrieve and validate the updated record - const [updatedRecord] = await db - .select() - .from(storageSchema) - .where(eq(storageSchema.key, 'update-key')); - - expect(updatedRecord.updatedAt).toBe(updatedAt); - }); - - it('should delete a record logically by setting deletedAt', async () => { - const record = { - id: 'delete-id', - key: 'delete-key', - originalName: 'delete-file.txt', - size: 2048, - mimeType: 'image/jpeg', - hash: 'delete-hash', - }; - - // Insert the record - await db.insert(storageSchema).values(record); - - // Logically delete the record by setting deletedAt - const deletedAt = new Date().toISOString(); - await db - .update(storageSchema) - .set({ deletedAt }) - .where(eq(storageSchema.key, 'delete-key')); - - // Retrieve the logically deleted record - const [deletedRecord] = await db - .select() - .from(storageSchema) - .where(eq(storageSchema.key, 'delete-key')); - - // Validate that deletedAt is set - expect(deletedRecord.deletedAt).toBe(deletedAt); - }); - - it('should ensure idx_r2_storage_key index is defined on the key column', async () => { - // Query the database to check for indexes - const indexes = await db - .run(sql`PRAGMA index_info('idx_r2_storage_key')`) - .execute(); - - // Validate the index exists and is associated with the correct column - const indexColumns = indexes?.results?.map?.( - (index: { name: string }) => index.name, - ); - expect(indexColumns).toContain('key'); - }); - - it('should validate idx_r2_storage_key index exists in the storage table', async () => { - // Query all indexes in the storage table - const tableIndexes = await db - .run(sql`PRAGMA index_list('storage')`) - .execute(); - - // Validate the index exists in the table - const indexNames = tableIndexes?.results?.map?.( - (index: { name: string }) => index.name, - ); - expect(indexNames).toContain('idx_r2_storage_key'); - }); -}); diff --git a/packages/database/src/__tests__/scripts/global-setup.ts b/packages/database/src/__tests__/scripts/global-setup.ts deleted file mode 100644 index 5cf7b3a..0000000 --- a/packages/database/src/__tests__/scripts/global-setup.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Miniflare } from 'miniflare'; -import { beforeAll, afterAll } from 'vitest'; -import { AnyD1Database } from 'drizzle-orm/d1'; -import { execMigrations } from './utils'; - -let mf: Miniflare; -let db: AnyD1Database; - -export const setupMiniflare = async () => { - mf = new Miniflare({ - modules: true, - d1Databases: { - DB: ':memory:', // Use an in-memory SQLite database for testing - }, - script: ` - export default { - async fetch(request, env, ctx) { - return new Response("Hello Miniflare!"); - } - } - `, - }); - try { - const env = await mf.getBindings(); - db = env.DB; // Get the D1 database binding from Miniflare - - // Apply migrations from the migrations directory - await execMigrations(db); - - console.log('Migrations applied to in-memory database.'); - } catch (ex) { - console.error(ex); - } -}; - -export const disposeMiniflare = async () => await mf.dispose(); // Clean up Miniflare - -beforeAll(setupMiniflare); -afterAll(disposeMiniflare); - -export const getTestDatabase = () => db; // Provide access to the test database diff --git a/packages/database/src/__tests__/scripts/migration-runner.ts b/packages/database/src/__tests__/scripts/migration-runner.ts deleted file mode 100644 index ee5ba60..0000000 --- a/packages/database/src/__tests__/scripts/migration-runner.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { readFile, readdir } from 'node:fs/promises'; -import type { AnyD1Database } from 'drizzle-orm/d1'; -import path from 'path'; - -export const applyMigrations = async ( - db: AnyD1Database, - migrationsDir: string, -) => { - const migrationFiles = await readdir(migrationsDir); - console.log(migrationFiles); - - for (const file of migrationFiles) { - if (file.endsWith('.sql')) { - const migrationPath = path.join(migrationsDir, file); - const sql = await readFile(migrationPath, 'utf-8'); - console.log(`Applying migration: ${file}`); - await db.prepare(sql).all(); - } - } -}; diff --git a/packages/database/src/__tests__/scripts/utils.ts b/packages/database/src/__tests__/scripts/utils.ts deleted file mode 100644 index 8d18775..0000000 --- a/packages/database/src/__tests__/scripts/utils.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { join, dirname } from 'node:path'; -import { AnyD1Database } from 'drizzle-orm/d1'; -import { applyMigrations } from './migration-runner'; -import { fileURLToPath } from 'node:url'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); - -export const execMigrations = async (db: AnyD1Database) => { - await applyMigrations(db, join(__dirname, '..', '..', '..', 'migrations')); -}; diff --git a/packages/database/src/__tests__/services/storage.service.e2e.test.ts b/packages/database/src/__tests__/services/storage.service.e2e.test.ts deleted file mode 100644 index 445a027..0000000 --- a/packages/database/src/__tests__/services/storage.service.e2e.test.ts +++ /dev/null @@ -1,308 +0,0 @@ -import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { AnyD1Database, drizzle } from 'drizzle-orm/d1'; -import { storageSchema } from '@schema/storage.schema'; -import { - clearStorageRecords, - createStorageRecord, - getStorageRecordFromKey, - listStorageRecords, -} from '@services/storage.service'; -import { getTestDatabase } from '../scripts/global-setup'; -import { v7 as uuidv7 } from 'uuid'; -import { eq } from 'drizzle-orm'; - -vi.mock('uuid', () => ({ - v7: vi.fn(), -})); - -let db: AnyD1Database; - -beforeEach(async () => { - // Initialize the test database with Drizzle ORM - db = drizzle(getTestDatabase()); - vi.resetAllMocks(); // Reset all mocks before each test - - // Optionally, clear existing data from the table to ensure each test starts clean - await db.delete(storageSchema); -}); - -describe('Storage Service Tests', () => { - describe('createStorageRecord', () => { - it('should successfully create a storage record', async () => { - const mockUuid = 'mock-uuid'; - vi.mocked(uuidv7 as () => string).mockReturnValue(mockUuid); - - const newRecord = { - key: 'file-key', - originalName: 'test-file.txt', - size: 1024, - mimeType: 'text/plain', - hash: 'hash-value', - }; - - // Create the record - const createdRecord = await createStorageRecord(newRecord, db); - - // Validate the created record - expect(createdRecord).toMatchObject({ - ...newRecord, - id: mockUuid, - }); - - // Validate the record exists in the database - const result = await db - .select() - .from(storageSchema) - .where(eq(storageSchema.key, 'file-key')); - - expect(result).toHaveLength(1); - expect(result[0]).toMatchObject({ - ...newRecord, - id: mockUuid, - createdAt: expect.any(String), - updatedAt: null, - deletedAt: null, - }); - }); - - it('should throw an error if the record creation fails', async () => { - const invalidRecord = { - key: null, // Null value violates NOT NULL constraint - originalName: 'invalid-file.txt', - size: 512, - mimeType: 'application/json', - hash: 'invalid-hash', - }; - - // Attempt to create an invalid record - await expect( - // @ts-expect-error we are testing the error case when key is null - createStorageRecord(invalidRecord, db), - ).rejects.toThrow(); - }); - }); - - describe('getStorageRecordFromKey', () => { - it('should retrieve a storage record by key', async () => { - const mockRecord = { - id: 'test-id', - key: 'test-key', - originalName: 'test-file.txt', - size: 2048, - mimeType: 'text/plain', - hash: 'hash-value', - createdAt: '2024-01-01T00:00:00Z', - updatedAt: null, - deletedAt: null, - }; - - // Insert the mock record - await db.insert(storageSchema).values(mockRecord); - - // Retrieve the record - const retrievedRecord = await getStorageRecordFromKey('test-key', db); - - // Validate the retrieved record - expect(retrievedRecord).toMatchObject(mockRecord); - }); - - it('should return null if the record does not exist', async () => { - const retrievedRecord = await getStorageRecordFromKey( - 'non-existent-key', - db, - ); - expect(retrievedRecord).toBeNull(); - }); - - it('should throw an error if the query fails', async () => { - // Mock an invalid database schema - vi.spyOn(db, 'select').mockImplementationOnce(() => { - throw new Error('Query failed'); - }); - - await expect(getStorageRecordFromKey('test-key', db)).rejects.toThrow( - 'Query failed', - ); - }); - }); - - describe('createStorageRecord (Throw Scenario)', () => { - it('should throw the response if insert fails', async () => { - const mockUuid = 'mock-uuid'; - vi.mocked(uuidv7 as () => string).mockReturnValue(mockUuid); - - const newRecord = { - key: 'invalid-key', // Some valid mock data - originalName: 'test-file.txt', - size: 1024, - mimeType: 'text/plain', - hash: 'hash-value', - }; - - // Mock the `db.insert()` method to return a failed response - vi.spyOn(db, 'insert').mockImplementation(() => ({ - values: vi.fn().mockReturnValueOnce({ - success: false, // Simulating failure - }), - })); - - // Ensure the function throws the response - await expect(createStorageRecord(newRecord, db)).rejects.toThrow(); - - // Restore the mocked implementation after the test - vi.restoreAllMocks(); - }); - }); - - describe('listStorageRecords', () => { - it('should return an empty array if no records exist', async () => { - // listStorageRecords should return [] - const records = await listStorageRecords(db); - expect(records).toEqual([]); - }); - - it('should return all records that are not deleted', async () => { - const mockRecords = [ - { - id: 'record-1', - key: 'key-1', - originalName: 'file1.txt', - size: 100, - mimeType: 'text/plain', - hash: 'hash1', - createdAt: new Date().toISOString(), - updatedAt: null, - deletedAt: null, - }, - { - id: 'record-2', - key: 'key-2', - originalName: 'file2.txt', - size: 200, - mimeType: 'text/plain', - hash: 'hash2', - createdAt: new Date().toISOString(), - updatedAt: null, - deletedAt: null, - }, - ]; - - // Insert multiple records - await db.insert(storageSchema).values(mockRecords); - - // Retrieve them - const records = await listStorageRecords(db); - - // Expect to find both since none are deleted - expect(records).toHaveLength(2); - expect(records).toEqual( - expect.arrayContaining([ - expect.objectContaining(mockRecords[0]), - expect.objectContaining(mockRecords[1]), - ]), - ); - }); - - it('should not return records that have a non-null deletedAt', async () => { - const mockRecord = { - id: 'record-deleted', - key: 'deleted-key', - originalName: 'file-deleted.txt', - size: 300, - mimeType: 'text/plain', - hash: 'hash3', - createdAt: new Date().toISOString(), - updatedAt: null, - // Mark it as deleted - deletedAt: new Date().toISOString(), - }; - - const activeRecord = { - id: 'record-active', - key: 'active-key', - originalName: 'file-active.txt', - size: 400, - mimeType: 'text/plain', - hash: 'hash4', - createdAt: new Date().toISOString(), - updatedAt: null, - deletedAt: null, - }; - - // Insert both a soft-deleted record and an active record - await db.insert(storageSchema).values([mockRecord, activeRecord]); - - // listStorageRecords should only return the active record - const records = await listStorageRecords(db); - expect(records).toHaveLength(1); - expect(records[0]).toMatchObject(activeRecord); - }); - - it('should throw an error if the query fails', async () => { - // Mock (or spy on) db.select() so it throws an error - vi.spyOn(db, 'select').mockImplementationOnce(() => { - throw new Error('List records query failed'); - }); - - // Because listStorageRecords() re-throws the error, - // we expect the promise to reject - await expect(listStorageRecords(db)).rejects.toThrow( - 'List records query failed', - ); - }); - }); - - describe('clearStorageRecords', () => { - it('should return an empty array after clearing the storage', async () => { - // listStorageRecords should return [] - const mockRecords = [ - { - id: 'record-1', - key: 'key-1', - originalName: 'file1.txt', - size: 100, - mimeType: 'text/plain', - hash: 'hash1', - createdAt: new Date().toISOString(), - updatedAt: null, - deletedAt: null, - }, - { - id: 'record-2', - key: 'key-2', - originalName: 'file2.txt', - size: 200, - mimeType: 'text/plain', - hash: 'hash2', - createdAt: new Date().toISOString(), - updatedAt: null, - deletedAt: null, - }, - ]; - // Insert multiple records - await db.insert(storageSchema).values(mockRecords); - // Retrieve them - const records = await listStorageRecords(db); - - // Expect to find both since none are deleted - expect(records).toHaveLength(2); - clearStorageRecords(db); - - const postDeletedRecords = await listStorageRecords(db); - expect(postDeletedRecords).toHaveLength(0); - }); - - it('should throw an error if the delete fails', async () => { - // Mock (or spy on) db.select() so it throws an error - vi.spyOn(db, 'delete').mockImplementationOnce(() => { - throw new Error('Delete records query failed'); - }); - - // Because listStorageRecords() re-throws the error, - // we expect the promise to reject - await expect(clearStorageRecords(db)).rejects.toThrow( - 'Delete records query failed', - ); - }); - }); -}); diff --git a/packages/database/src/client.ts b/packages/database/src/client.ts deleted file mode 100644 index f371812..0000000 --- a/packages/database/src/client.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { type AnyD1Database, drizzle, DrizzleD1Database } from 'drizzle-orm/d1'; - -const drizzleDBMap = new WeakMap(); - -export const getDBClient = async ( - reference: object, - DB: AnyD1Database, -): Promise => { - let drizzleInstance = drizzleDBMap.get(reference); - if (drizzleInstance) { - return drizzleInstance; - } - drizzleInstance = drizzle(DB); - drizzleDBMap.set(reference, drizzleInstance); - return drizzleInstance; -}; diff --git a/packages/database/src/index.ts b/packages/database/src/index.ts index 406b4f0..3996267 100644 --- a/packages/database/src/index.ts +++ b/packages/database/src/index.ts @@ -1,3 +1,26 @@ -export * from './client'; -export * from './services/storage.service'; -export * from './schema/storage.schema'; +import { drizzle } from 'drizzle-orm/d1'; +import { createFlarekitServices } from './proxy'; +import * as services from './services'; +import type { Ctx } from './types'; + +const drizzleDBMap = new WeakMap(); + +export const getInstance = (reference: object) => { + return drizzleDBMap.get(reference); +}; +export const initDBInstance = (reference: object, env: Env) => { + let instance = drizzleDBMap.get(reference); + + if (!instance) { + const db = drizzle(env.DB); + instance = { + db, + cache: env.CACHE, + queue: env.QUEUE, + }; + drizzleDBMap.set(reference, instance); + } + + // Wrap the "services" in the "createFlarekitServices" proxy + return createFlarekitServices(instance, services); +}; diff --git a/packages/database/src/proxy.ts b/packages/database/src/proxy.ts new file mode 100644 index 0000000..e97fe1b --- /dev/null +++ b/packages/database/src/proxy.ts @@ -0,0 +1,59 @@ +import type { Ctx } from './types'; + +/** + * OmitCtxParam: + * Given a function type that has the signature (...args, Ctx) => ReturnType, + * produce a new function type that omits the final Ctx param. + */ +type OmitCtxParam = T extends (...args: [...infer Rest, Ctx]) => infer R + ? (...args: Rest) => R + : T; + +/** + * TransformNamespace: + * Given an object { fnA, fnB, ... }, + * convert each function to the version that omits the final Ctx param. + */ +type TransformNamespace = { + [K in keyof TNamespace]: OmitCtxParam; +}; + +/** + * FlarekitServices: + * For each namespace in `TServices` (e.g. `user`, `posts`, etc.), + * transform all functions within that namespace. + */ +type FlarekitServices = { + [N in keyof TServices]: TransformNamespace; +}; + +export function createFlarekitServices( + ctx: Ctx, + services: TServices, +): FlarekitServices { + // 1. Create an empty object to hold the โ€œwrappedโ€ namespaces + const result = {} as FlarekitServices; + + // 2. Iterate over each namespace in `services` + for (const [namespaceKey, namespaceObj] of Object.entries(services)) { + // Assume each "namespace" is an object of functions + possibly other stuff + const transformedNamespace = {} as Record; + + // 3. Shallow-clone / wrap each property + for (const [fnKey, fnVal] of Object.entries(namespaceObj)) { + if (typeof fnVal === 'function') { + // Return a function that calls the original with the appended `ctx` + transformedNamespace[fnKey] = (...args: unknown[]) => + (fnVal as Function)(...args, ctx); + } else { + // If itโ€™s not a function, just copy it as-is + transformedNamespace[fnKey] = fnVal; + } + } + + // 4. Insert the transformed namespace into the result + (result as any)[namespaceKey] = transformedNamespace; + } + + return result; +} diff --git a/packages/database/src/schemas.ts b/packages/database/src/schemas.ts new file mode 100644 index 0000000..554ab32 --- /dev/null +++ b/packages/database/src/schemas.ts @@ -0,0 +1 @@ +export * from './schema/storage.schema'; diff --git a/packages/database/src/services.ts b/packages/database/src/services.ts new file mode 100644 index 0000000..5698e7f --- /dev/null +++ b/packages/database/src/services.ts @@ -0,0 +1,3 @@ +import * as storage from '@services/storage.service'; + +export { storage }; diff --git a/packages/database/src/services/storage.service.ts b/packages/database/src/services/storage.service.ts index 6d65228..0f32dcb 100644 --- a/packages/database/src/services/storage.service.ts +++ b/packages/database/src/services/storage.service.ts @@ -1,9 +1,7 @@ import { storageSchema, type InsertStorageType } from '@schema/storage.schema'; import { and, eq, isNull, desc } from 'drizzle-orm'; import { v7 as uuidv7 } from 'uuid'; -import { DB } from '../@types/database.type'; - -// type DB = DrizzleD1Database; +import { Ctx } from '../types'; /** * Creates a new storage record in the database. @@ -14,11 +12,11 @@ import { DB } from '../@types/database.type'; */ export const createStorageRecord = async ( storageRecord: Omit, - db: DB, + ctx: Ctx, ) => { try { const uid = uuidv7(); - const response = await db.insert(storageSchema).values({ + const response = await ctx.db.insert(storageSchema).values({ ...storageRecord, id: uid, }); @@ -42,11 +40,11 @@ export const createStorageRecord = async ( * @returns The storage record if found, otherwise null. * @throws Error if the retrieval fails. */ -export const getStorageRecordFromKey = async (key: string, db: DB) => { +export const getStorageRecordFromKey = async (key: string, ctx: Ctx) => { try { return ( ( - await db + await ctx.db .select() .from(storageSchema) .where( @@ -67,9 +65,9 @@ export const getStorageRecordFromKey = async (key: string, db: DB) => { * @returns Array of all Storage Records. * @throws Error if the listing fails. */ -export const listStorageRecords = async (db: DB) => { +export const listStorageRecords = async (ctx: Ctx) => { try { - return await db + return await ctx.db .select() .from(storageSchema) .where(isNull(storageSchema.deletedAt)) @@ -86,9 +84,9 @@ export const listStorageRecords = async (db: DB) => { * @returns The number of records deleted. * @throws Error if the deletion fails. */ -export const clearStorageRecords = async (db: DB) => { +export const clearStorageRecords = async (ctx: Ctx) => { try { - return await db.delete(storageSchema); + return await ctx.db.delete(storageSchema); } catch (err) { console.error(err); throw err; diff --git a/packages/database/src/types.ts b/packages/database/src/types.ts new file mode 100644 index 0000000..a6d3d27 --- /dev/null +++ b/packages/database/src/types.ts @@ -0,0 +1,8 @@ +// types.ts +import type { DrizzleD1Database } from 'drizzle-orm/d1'; + +export interface Ctx { + db: DrizzleD1Database; + cache?: KVNamespace; + queue?: Queue; +} diff --git a/packages/database/test/apply-migrations.ts b/packages/database/test/apply-migrations.ts new file mode 100644 index 0000000..c25eddf --- /dev/null +++ b/packages/database/test/apply-migrations.ts @@ -0,0 +1,6 @@ +import { applyD1Migrations, env } from 'cloudflare:test'; + +// Setup files run outside isolated storage, and may be run multiple times. +// `applyD1Migrations()` only applies migrations that haven't already been +// applied, therefore it is safe to call this function here. +await applyD1Migrations(env.DB, env.TEST_MIGRATIONS); diff --git a/packages/database/test/env.d.ts b/packages/database/test/env.d.ts new file mode 100644 index 0000000..d22f4a1 --- /dev/null +++ b/packages/database/test/env.d.ts @@ -0,0 +1,5 @@ +declare module "cloudflare:test" { + interface ProvidedEnv extends Env { + TEST_MIGRATIONS: D1Migration[]; + } +} \ No newline at end of file diff --git a/packages/database/test/index.test.ts b/packages/database/test/index.test.ts new file mode 100644 index 0000000..80e0152 --- /dev/null +++ b/packages/database/test/index.test.ts @@ -0,0 +1,37 @@ +import { createExecutionContext, env } from 'cloudflare:test'; +import { describe, it, expect } from 'vitest'; +import { initDBInstance } from '../src'; + +describe('Initialized Database', () => { + it('Should be initilized without errors', async () => { + const ctx = createExecutionContext(); + const db = initDBInstance(ctx, env); + expect(db).toBeDefined(); + + const db2 = initDBInstance(ctx, env); + expect(db2).toBeDefined(); + }); + + // Hypothetical usage + it('Should create an item in storage', async () => { + const ctx = createExecutionContext(); + const db = initDBInstance(ctx, env); + + const record = await db.storage.createStorageRecord({ + key: 'myKey', + originalName: 'example.jpg', + size: 12345, + mimeType: 'image/jpeg', + hash: 'abc123hash', + }); + + // Validate the returned record + expect(record).toBeDefined(); + expect(record).toHaveProperty('id'); + expect(record.key).toBe('myKey'); + expect(record.originalName).toBe('example.jpg'); + expect(record.size).toBe(12345); + expect(record.mimeType).toBe('image/jpeg'); + expect(record.hash).toBe('abc123hash'); + }); +}); diff --git a/packages/database/test/proxy.test.ts b/packages/database/test/proxy.test.ts new file mode 100644 index 0000000..9e78d9c --- /dev/null +++ b/packages/database/test/proxy.test.ts @@ -0,0 +1,53 @@ +import { describe, it, expect, vi } from 'vitest'; +import { createFlarekitServices } from '../src/proxy'; +import type { Ctx } from '../src/types'; +import { env, createExecutionContext } from 'cloudflare:test'; +import { drizzle } from 'drizzle-orm/d1'; + +describe('createFlarekitServices', () => { + it('should wrap each function to omit the final Ctx param and preserve non-function props', () => { + // Mocked Ctx + const ctx = { + db: drizzle(env.DB), + cache: env.CACHE, + queue: env.QUEUE, + }; + + // Example original functions + const addFn = vi.fn((a: number, b: number, _: Ctx) => a + b); + const greetFn = vi.fn((name: string, _: Ctx) => `Hello, ${name}`); + + // Example of a nested โ€œnamespaceโ€ object + const services = { + math: { + add: addFn, + pi: 3.14, // Non-function prop + }, + messages: { + greet: greetFn, + greetingPrefix: 'Hello, ', // Non-function prop + }, + }; + + // Wrap the services + const wrappedServices = createFlarekitServices(ctx, services); + + // 1. Check that the wrapped versions are indeed functions (for function props) + expect(typeof wrappedServices.math.add).toBe('function'); + expect(typeof wrappedServices.messages.greet).toBe('function'); + + // 2. Check that non-function properties are copied over + expect(wrappedServices.math.pi).toBe(3.14); + expect(wrappedServices.messages.greetingPrefix).toBe('Hello, '); + + // 3. Call the newly wrapped functions without providing ctx + const sum = wrappedServices.math.add(2, 3); + expect(sum).toBe(5); // 2 + 3 = 5 + // The original addFn should have been called with the appended ctx + expect(addFn).toHaveBeenCalledWith(2, 3, ctx); + + const greeting = wrappedServices.messages.greet('Alice'); + expect(greeting).toBe('Hello, Alice'); + expect(greetFn).toHaveBeenCalledWith('Alice', ctx); + }); +}); diff --git a/packages/database/test/services/storage.service.test.ts b/packages/database/test/services/storage.service.test.ts new file mode 100644 index 0000000..c2f631c --- /dev/null +++ b/packages/database/test/services/storage.service.test.ts @@ -0,0 +1,217 @@ +import { createExecutionContext, env } from 'cloudflare:test'; +import { describe, it, expect, vi, afterEach } from 'vitest'; + +import { getInstance, initDBInstance } from '../../src'; +import { validate } from 'uuid'; + +const ctx = createExecutionContext(); +const db = initDBInstance(ctx, env); + +describe('storage.service', () => { + afterEach(() => { + // Reset mocks between tests + vi.restoreAllMocks(); + vi.clearAllMocks(); + }); + + describe('createStorageRecord', () => { + it('should create a new storage record with a generated UUID and return it (success)', async () => { + const input = { + key: 'test-key', + originalName: 'test.txt', + size: 123, + mimeType: 'text/plain', + hash: 'somehash', + }; + + const result = await db.storage.createStorageRecord(input); + expect(validate(result.id)).toBe(true); + }); + + it('Should throw an error if insertion fails', async () => { + const input = { + key: 'bad-key', + originalName: 'bad.txt', + size: 999, + mimeType: 'text/plain', + hash: 'bad-hash', + }; + const instance = getInstance(ctx); + if (!instance) { + throw new Error( + 'Ctx instance not found. Make sure initDatabase is implemented', + ); + } + + vi.spyOn(instance.db, 'insert').mockImplementation(() => { + throw new Error('DB Insert Failed'); + }); + + await expect(db.storage.createStorageRecord(input)).rejects.toThrow( + 'DB Insert Failed', + ); + }); + + it('should throw an error if response.success is false', async () => { + const instance = getInstance(ctx); + if (!instance) { + throw new Error( + 'Ctx instance not found. Make sure initDatabase is implemented', + ); + } + // 1. Mock the db.insert(...) chain to return { success: false, reason: "some-error" } + vi.spyOn(instance.db, 'insert').mockImplementation(() => { + return { + values: vi.fn().mockResolvedValue({ + success: false, + reason: 'some-error', + }), + } as any; + }); + + // 2. Prepare your input + const input = { + key: 'somekey', + originalName: 'somefile', + size: 100, + mimeType: 'application/json', + hash: 'zzz', + }; + + // 3. Assert that createStorageRecord rejects with the expected error object + await expect(db.storage.createStorageRecord(input)).rejects.toEqual({ + success: false, + reason: 'some-error', + }); + }); + }); + + describe('getStorageRecordFromKey', () => { + it('should return the first matched record if found', async () => { + const input = { + key: 'test-key', + originalName: 'somefile', + size: 100, + mimeType: 'application/json', + hash: 'zzz', + }; + await db.storage.createStorageRecord(input); + + const result = await db.storage.getStorageRecordFromKey('test-key'); + + expect(result).toBeDefined(); + }); + + it('should return null if no record is found', async () => { + const result = + await db.storage.getStorageRecordFromKey('non-existent-key'); + expect(result).toBeNull(); + }); + + it('should throw an error if the query fails', async () => { + const instance = getInstance(ctx); + if (!instance) { + throw new Error( + 'Ctx instance not found. Make sure initDatabase is implemented', + ); + } + // 1. Mock the db.insert(...) chain to return { success: false, reason: "some-error" } + vi.spyOn(instance.db, 'select').mockImplementation(() => { + throw new Error('DB Select Failed'); + }); + await expect(db.storage.getStorageRecordFromKey('foo')).rejects.toThrow( + 'DB Select Failed', + ); + }); + }); + + describe('listStorageRecords', () => { + it('should list all active (non-deleted) records in descending order of createdAt', async () => { + const input = { + key: 'test-key-1', + originalName: 'somefile', + size: 100, + mimeType: 'application/json', + hash: 'zzz', + }; + const input2 = { + key: 'test-key-2', + originalName: 'somefile', + size: 100, + mimeType: 'application/json', + hash: 'aaa', + }; + await db.storage.clearStorageRecords(); + const record1 = await db.storage.createStorageRecord(input); + const record2 = await db.storage.createStorageRecord(input2); + + const result = await db.storage.listStorageRecords(); + + expect(result).toHaveLength(2); + expect(result[0].id).eq(record1.id); + expect(result[1].id).eq(record2.id); + }); + + it('should throw if the listing fails', async () => { + const instance = getInstance(ctx); + if (!instance) { + throw new Error( + 'Ctx instance not found. Make sure initDatabase is implemented', + ); + } + vi.spyOn(instance.db, 'select').mockImplementation(() => { + throw new Error('DB Select Error'); + }); + await expect(db.storage.listStorageRecords()).rejects.toThrow( + 'DB Select Error', + ); + }); + }); + + describe('clearStorageRecords', () => { + it('should delete all records from the storage table', async () => { + const input = { + key: 'test-key-1', + originalName: 'somefile', + size: 100, + mimeType: 'application/json', + hash: 'zzz', + }; + const input2 = { + key: 'test-key-2', + originalName: 'somefile', + size: 100, + mimeType: 'application/json', + hash: 'aaa', + }; + await db.storage.clearStorageRecords(); + await db.storage.createStorageRecord(input); + await db.storage.createStorageRecord(input2); + + const records = await db.storage.listStorageRecords(); + expect(records).toHaveLength(2); + + await db.storage.clearStorageRecords(); + + const postDeleteRecords = await db.storage.listStorageRecords(); + expect(postDeleteRecords).toHaveLength(0); + }); + + it('should throw if the deletion fails', async () => { + const instance = getInstance(ctx); + if (!instance) { + throw new Error( + 'Ctx instance not found. Make sure initDatabase is implemented', + ); + } + // 1. Mock the db.insert(...) chain to return { success: false, reason: "some-error" } + vi.spyOn(instance.db, 'delete').mockImplementation(() => { + throw new Error('DB Delete Error'); + }); + await expect(db.storage.clearStorageRecords()).rejects.toThrow( + 'DB Delete Error', + ); + expect(instance.db.delete).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/packages/database/test/tsconfig.json b/packages/database/test/tsconfig.json new file mode 100644 index 0000000..c741be3 --- /dev/null +++ b/packages/database/test/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "moduleResolution": "bundler", + "types": [ + "@cloudflare/workers-types/experimental", + "@cloudflare/vitest-pool-workers", + "../worker-configuration.d.ts" + ] + }, + "include": ["./**/*.ts", "../src/env.d.ts"] +} diff --git a/packages/database/tsconfig.json b/packages/database/tsconfig.json index d389c34..71bf221 100644 --- a/packages/database/tsconfig.json +++ b/packages/database/tsconfig.json @@ -2,22 +2,18 @@ "compilerOptions": { "target": "ESNext", "module": "ESNext", - "moduleResolution": "node", + "moduleResolution": "Bundler", + "strict": true, "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "declarationDir": "./dist/types", "outDir": "dist", - "rootDir": "src", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, + "lib": ["ESNext"], + "types": ["@cloudflare/workers-types"], "paths": { "@schema/*": ["./src/schema/*"], "@services/*": ["./src/services/*"] } }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist"] + "include": ["worker-configuration.d.ts", "src/**/*.ts"] } diff --git a/packages/database/tsconfig.test.json b/packages/database/tsconfig.test.json deleted file mode 100644 index 2530703..0000000 --- a/packages/database/tsconfig.test.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "declarationDir": "./dist/__tests__/scripts/types" - } -} diff --git a/packages/database/vitest.config.ts b/packages/database/vitest.config.ts index b00d526..27c43f4 100644 --- a/packages/database/vitest.config.ts +++ b/packages/database/vitest.config.ts @@ -1,27 +1,39 @@ -import { defineConfig } from 'vitest/config'; import tsconfigPaths from 'vite-tsconfig-paths'; +import { + defineWorkersConfig, + readD1Migrations, +} from '@cloudflare/vitest-pool-workers/config'; +import { join } from 'node:path'; -export default defineConfig({ - test: { - globals: true, - setupFiles: ['./src/__tests__/scripts/global-setup.ts'], // Add global setup here - coverage: { - provider: 'istanbul', - exclude: [ - '**/dist/**', // Exclude dist directories - '**/tests/**', // Exclude tests directories - '**/__tests__/**', // Exclude tests directories - '**/*.test.{ts,js}', // Exclude test files - '**/*.d.ts', // Exclude TypeScript declaration files - 'node_modules/**', // Exclude dependencies - ], - thresholds: { - lines: 80, - functions: 80, - branches: 80, - statements: 80, +const __dirname = new URL('.', import.meta.url).pathname; + +export default defineWorkersConfig(async (_) => { + const migrationsPath = join(__dirname, 'migrations'); + const migrations = await readD1Migrations(migrationsPath); + return { + test: { + globals: true, + setupFiles: ['./test/apply-migrations.ts'], + poolOptions: { + workers: { + wrangler: { configPath: './wrangler.json' }, + miniflare: { + // Add a test-only binding for migrations, so we can apply them in a + // setup file + bindings: { TEST_MIGRATIONS: migrations }, + }, + }, + }, + coverage: { + provider: 'istanbul', + thresholds: { + lines: 90, + functions: 90, + branches: 90, + statements: 90, + }, }, }, - }, - plugins: [tsconfigPaths()], + plugins: [tsconfigPaths()], + }; }); diff --git a/packages/database/worker-configuration.d.ts b/packages/database/worker-configuration.d.ts new file mode 100644 index 0000000..182d249 --- /dev/null +++ b/packages/database/worker-configuration.d.ts @@ -0,0 +1,9 @@ +// Generated by Wrangler by running `wrangler types` + +interface Env { + CACHE: KVNamespace; + PUBLIC_CDN_URL: string; + STORAGE: R2Bucket; + DB: D1Database; + QUEUE: Queue; +} diff --git a/packages/database/wrangler.config.json b/packages/database/wrangler.config.json new file mode 100644 index 0000000..796bcc0 --- /dev/null +++ b/packages/database/wrangler.config.json @@ -0,0 +1,4 @@ +{ + "$schema": "../../node_modules/wrangler/config-schema.json", + "name": "flarekit-database" +} diff --git a/turbo.json b/turbo.json index f452961..77d4fd3 100644 --- a/turbo.json +++ b/turbo.json @@ -2,32 +2,32 @@ "$schema": "https://turbo.build/schema.json", "ui": "tui", "tasks": { - "@services/database#build": { + "@flarekit/database#build": { "cache": false, "outputs": ["dist/**"] }, - "@services/database#dev": { - "dependsOn": ["@services/database#build"], + "@flarekit/database#dev": { + "dependsOn": ["@flarekit/database#build"], "cache": false, "persistent": true, "outputs": [] }, - "@apps/web#dev": { - "dependsOn": ["@services/database#build"], + "@flarekit/web#dev": { + "dependsOn": ["@flarekit/database#build"], "cache": false, "persistent": true, "outputs": [] }, "dev": { - "dependsOn": ["@services/database#build"], + "dependsOn": ["@flarekit/database#build"], "cache": false, "outputs": [] }, "dev:web": { "dependsOn": [ - "@services/database#dev", - "@apps/web#dev", - "@apps/worker#dev" + "@flarekit/database#dev", + "@flarekit/web#dev", + "@flarekit/worker#dev" ], "persistent": true, "cache": false, @@ -40,7 +40,7 @@ "outputs": [] }, "deploy:web": { - "dependsOn": ["@services/database#build", "@apps/web#build"], + "dependsOn": ["@flarekit/database#build", "@flarekit/web#build"], "inputs": ["$TURBO_DEFAULT$", ".env*"], "outputs": ["dist/**"] }, diff --git a/wrangler.json b/wrangler.json index f26d84a..44f19f4 100644 --- a/wrangler.json +++ b/wrangler.json @@ -1,6 +1,6 @@ { "$schema": "./node_modules/wrangler/config-schema.json", - "name": "astroflare", + "name": "flarekit", "compatibility_date": "2024-09-02", "compatibility_flags": ["nodejs_compat"], "kv_namespaces": [ @@ -28,7 +28,7 @@ "producers": [ { "binding": "QUEUE", - "queue": "astroflare-queue" + "queue": "flarekit-queue" } ] }