From 32f5d216ba381652f62785ee17dca172c8a048ac Mon Sep 17 00:00:00 2001 From: taogaetz <59668529+taogaetz@users.noreply.github.com> Date: Mon, 8 Sep 2025 14:23:31 -0400 Subject: [PATCH] add markdown and docker --- .dockerignore | 95 +++++++ DEPLOYMENT.md | 80 ++++++ Dockerfile | 88 ++++++ docker-entrypoint.sh | 18 ++ package-lock.json | 331 +++++++++++++++-------- package.json | 1 + prisma/seed.ts | 8 +- server.mjs | 13 + src/app.html | 2 +- src/hooks.server.ts | 5 +- src/lib/utils/markdown.ts | 101 +++++++ src/routes/+layout.svelte | 13 - src/routes/chef-access/+page.server.ts | 40 --- src/routes/chef-access/+page.svelte | 129 --------- src/routes/chef-cookie/+page.server.ts | 8 +- src/routes/health/+server.ts | 6 + src/routes/logout/+server.ts | 32 ++- src/routes/recipe/[id]/+page.svelte | 11 +- src/routes/recipe/[id]/edit/+page.svelte | 186 +++++++++++-- src/routes/recipe/[id]/edit/+server.ts | 71 ++--- src/routes/recipe/new/+page.svelte | 160 +++++++++-- src/routes/recipe/new/+server.ts | 71 ++--- svelte.config.js | 16 +- 23 files changed, 1063 insertions(+), 422 deletions(-) create mode 100644 .dockerignore create mode 100644 DEPLOYMENT.md create mode 100644 Dockerfile create mode 100644 docker-entrypoint.sh create mode 100644 server.mjs create mode 100644 src/lib/utils/markdown.ts delete mode 100644 src/routes/chef-access/+page.server.ts delete mode 100644 src/routes/chef-access/+page.svelte create mode 100644 src/routes/health/+server.ts diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..49bba25 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,95 @@ +# Dependencies +node_modules +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Build outputs +.svelte-kit +build +dist + +# Environment files +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# IDE files +.vscode +.idea +*.swp +*.swo + +# OS files +.DS_Store +Thumbs.db + +# Git +.git +.gitignore + +# Docker +Dockerfile +.dockerignore + +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Dependency directories +jspm_packages/ + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Database files (will be mounted as volume) +prisma/database.db + diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..cd58faf --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,80 @@ +# Chef Bible - Docker Deployment + +## Portainer Deployment + +This application is designed to be deployed on Portainer with SQLite database persistence. + +### Environment Variables + +Set these environment variables in Portainer: + +**Required:** +``` +NODE_ENV=production +DATABASE_URL=file:/app/data/database.db +ORIGIN=https://birba.redbackpack.ca +``` + +**Optional (for photo uploads):** +``` +CLOUDINARY_URL=cloudinary://api_key:api_secret@cloud_name +``` + +*Note: If CLOUDINARY_URL is not provided, photo uploads will be disabled but the application will still function normally.* + +### Volume Mounts + +Create a volume mount for database persistence: + +- **Host Path**: `/var/lib/docker/volumes/chef-bible-data/_data` +- **Container Path**: `/app/data` +- **Type**: Bind Mount or Named Volume + +### Port Configuration + +- **Container Port**: 3000 +- **Host Port**: 80 (or your preferred port) +- **Protocol**: HTTP + +### Docker Build + +To build the image locally: + +```bash +docker build -t chef-bible . +``` + +### Database Initialization + +The application will automatically: +1. Create the SQLite database on first run +2. Run Prisma migrations +3. Seed the database with initial recipes + +### Health Check + +The container includes a health check endpoint at `/health` that Portainer can use to monitor the application status. + +### Security Notes + +- The application runs as a non-root user (`sveltekit`) +- Database files are stored in a persistent volume +- Environment variables are used for configuration +- No sensitive data is hardcoded + +### Backup + +To backup the database: + +```bash +# Copy the database file from the volume +docker cp :/app/data/database.db ./backup-$(date +%Y%m%d).db +``` + +### Updates + +To update the application: +1. Build new image with updated code +2. Deploy new container in Portainer +3. Database will persist through updates via volume mount + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ca81bd4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,88 @@ +# Use Node.js 20 Alpine for smaller image size +FROM node:20-alpine AS base + +# Install dependencies only when needed +FROM base AS deps +# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. +RUN apk add --no-cache libc6-compat +WORKDIR /app + +# Install dependencies based on the preferred package manager +COPY package.json package-lock.json* ./ +RUN npm ci + +# Rebuild the source code only when needed +FROM base AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +# Accept build arguments for environment variables +ARG DATABASE_URL +ARG CLOUDINARY_URL +ARG MAGIC_LINK_TOKEN + +# Set environment variables for build +ENV DATABASE_URL=$DATABASE_URL +ENV CLOUDINARY_URL=$CLOUDINARY_URL +ENV MAGIC_LINK_TOKEN=$MAGIC_LINK_TOKEN + +# Generate Prisma client +RUN npx prisma generate + +# Build the application +RUN npm run build + +# Production image, copy all the files and run the app +FROM base AS runner +WORKDIR /app + +# Accept build arguments for environment variables +ARG DATABASE_URL +ARG CLOUDINARY_URL +ARG MAGIC_LINK_TOKEN + +ENV NODE_ENV=production +ENV PORT=3000 +ENV DATABASE_URL=$DATABASE_URL +ENV CLOUDINARY_URL=$CLOUDINARY_URL +ENV MAGIC_LINK_TOKEN=$MAGIC_LINK_TOKEN + +# Create a non-root user +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 sveltekit + +# Copy the built application +COPY --from=builder --chown=sveltekit:nodejs /app/build ./build +COPY --from=builder --chown=sveltekit:nodejs /app/server.mjs ./server.mjs +COPY --from=builder --chown=sveltekit:nodejs /app/package.json ./package.json +COPY --from=builder --chown=sveltekit:nodejs /app/package-lock.json ./package-lock.json +COPY --from=builder --chown=sveltekit:nodejs /app/prisma ./prisma +COPY --from=builder --chown=sveltekit:nodejs /app/node_modules/.prisma ./node_modules/.prisma +COPY --from=builder --chown=sveltekit:nodejs /app/docker-entrypoint.sh ./docker-entrypoint.sh + +# Keep "type": "module" for ES modules + +# Install only production dependencies +RUN npm ci --omit=dev && npm cache clean --force + +# Create directory for SQLite database +RUN mkdir -p /app/data && chown sveltekit:nodejs /app/data + +# Set the database URL to use the volume +ENV DATABASE_URL="file:/app/data/database.db" + +# Make entrypoint script executable +RUN chmod +x /app/docker-entrypoint.sh + +USER sveltekit + +EXPOSE 3000 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD node -e "require('http').get('http://localhost:3000/health', (res) => { process.exit(res.statusCode === 200 ? 0 : 1) })" + +ENTRYPOINT ["/app/docker-entrypoint.sh"] +CMD ["node", "server.mjs"] + diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100644 index 0000000..de411fc --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,18 @@ +#!/bin/sh +set -e + +# Wait for database to be ready (if using external database) +# For SQLite, this is not needed, but good practice for future database changes + +echo "Running database migrations..." +# Use the DATABASE_URL from environment or default to SQLite +if [ -z "$DATABASE_URL" ]; then + export DATABASE_URL="file:/app/data/database.db" +fi +npx prisma migrate deploy + +echo "Seeding database..." +npx prisma db seed + +echo "Starting application..." +exec "$@" diff --git a/package-lock.json b/package-lock.json index b527059..8c98d8a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@prisma/client": "^6.14.0", "@prisma/extension-accelerate": "^2.0.2", + "@sveltejs/adapter-node": "^5.3.1", "cloudinary": "^2.7.0", "convert": "^5.12.0" }, @@ -60,7 +61,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -77,7 +77,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -94,7 +93,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -111,7 +109,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -128,7 +125,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -145,7 +141,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -162,7 +157,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -179,7 +173,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -196,7 +189,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -213,7 +205,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -230,7 +221,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -247,7 +237,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -264,7 +253,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -281,7 +269,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -298,7 +285,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -315,7 +301,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -332,7 +317,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -349,7 +333,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -366,7 +349,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -383,7 +365,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -400,7 +381,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -417,7 +397,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -434,7 +413,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -451,7 +429,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -468,7 +445,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -485,7 +461,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -767,7 +742,6 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -778,7 +752,6 @@ "version": "2.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -789,7 +762,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -799,14 +771,12 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.30", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -852,7 +822,6 @@ "version": "1.0.0-next.29", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", - "dev": true, "license": "MIT" }, "node_modules/@prisma/client": { @@ -951,6 +920,107 @@ "@prisma/debug": "6.14.0" } }, + "node_modules/@rollup/plugin-commonjs": { + "version": "28.0.6", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.6.tgz", + "integrity": "sha512-XSQB1K7FUU5QP+3lOQmVCE3I0FcbbNvmNT4VJSj93iUjayaARrTQeoRdiYQoftAJBLrR9t2agwAd3ekaTgHNlw==", + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "fdir": "^6.2.0", + "is-reference": "1.2.1", + "magic-string": "^0.30.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=16.0.0 || 14 >= 14.17" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@rollup/plugin-json": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", + "integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==", + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.1.tgz", + "integrity": "sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==", + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.46.2", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz", @@ -958,7 +1028,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -972,7 +1041,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -986,7 +1054,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1000,7 +1067,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1014,7 +1080,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1028,7 +1093,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1042,7 +1106,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1056,7 +1119,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1070,7 +1132,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1084,7 +1145,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1098,7 +1158,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1112,7 +1171,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1126,7 +1184,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1140,7 +1197,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1154,7 +1210,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1168,7 +1223,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1182,7 +1236,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1196,7 +1249,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1210,7 +1262,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1224,7 +1275,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1235,14 +1285,12 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", - "devOptional": true, "license": "MIT" }, "node_modules/@sveltejs/acorn-typescript": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.5.tgz", "integrity": "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==", - "dev": true, "license": "MIT", "peerDependencies": { "acorn": "^8.9.0" @@ -1258,11 +1306,25 @@ "@sveltejs/kit": "^2.0.0" } }, + "node_modules/@sveltejs/adapter-node": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.3.1.tgz", + "integrity": "sha512-PSoGfa9atkmuixe7jvuS2tsUohVZF20So87ASzfMRGTTNqEd8s48KAodlv3CzHwq9XO/BM8KsQLpqqsr/6dmuA==", + "license": "MIT", + "dependencies": { + "@rollup/plugin-commonjs": "^28.0.1", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^16.0.0", + "rollup": "^4.9.5" + }, + "peerDependencies": { + "@sveltejs/kit": "^2.4.0" + } + }, "node_modules/@sveltejs/kit": { "version": "2.28.0", "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.28.0.tgz", "integrity": "sha512-qrhygwHV5r6JrvCw4gwNqqxYGDi5YbajocxfKgFXmSFpFo8wQobUvsM0OfakN4h+0LEmXtqHRrC6BcyAkOwyoQ==", - "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.0.0", @@ -1295,7 +1357,6 @@ "version": "6.1.2", "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.1.2.tgz", "integrity": "sha512-7v+7OkUYelC2dhhYDAgX1qO2LcGscZ18Hi5kKzJQq7tQeXpH215dd0+J/HnX2zM5B3QKcIrTVqCGkZXAy5awYw==", - "dev": true, "license": "MIT", "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", @@ -1317,7 +1378,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-5.0.0.tgz", "integrity": "sha512-iwQ8Z4ET6ZFSt/gC+tVfcsSBHwsqc6RumSaiLUkAurW3BCpJam65cmHw0oOlDMTO0u+PZi9hilBRYN+LZNHTUQ==", - "dev": true, "license": "MIT", "dependencies": { "debug": "^4.4.1" @@ -1612,14 +1672,12 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", - "dev": true, "license": "MIT" }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, "license": "MIT" }, "node_modules/@types/json-schema": { @@ -1633,7 +1691,6 @@ "version": "24.2.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz", "integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==", - "dev": true, "license": "MIT", "optional": true, "peer": true, @@ -1641,6 +1698,12 @@ "undici-types": "~7.10.0" } }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.39.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.1.tgz", @@ -1903,7 +1966,6 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -1991,7 +2053,6 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">= 0.4" @@ -2001,7 +2062,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">= 0.4" @@ -2158,7 +2218,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -2184,6 +2243,12 @@ "dev": true, "license": "MIT" }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "license": "MIT" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2221,7 +2286,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -2269,7 +2333,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2294,7 +2357,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -2328,7 +2390,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "engines": { "node": ">=8" @@ -2338,7 +2400,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.1.1.tgz", "integrity": "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==", - "dev": true, "license": "MIT" }, "node_modules/dotenv": { @@ -2393,7 +2454,6 @@ "version": "0.25.9", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", - "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -2589,7 +2649,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", - "dev": true, "license": "MIT" }, "node_modules/espree": { @@ -2627,7 +2686,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.1.0.tgz", "integrity": "sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" @@ -2656,6 +2714,12 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -2758,7 +2822,6 @@ "version": "6.4.6", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", - "dev": true, "license": "MIT", "peerDependencies": { "picomatch": "^3 || ^4" @@ -2846,11 +2909,20 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-tsconfig": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "resolve-pkg-maps": "^1.0.0" @@ -2926,6 +2998,18 @@ "node": ">=8" } }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -2975,6 +3059,21 @@ "node": ">=8" } }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2996,6 +3095,12 @@ "node": ">=0.10.0" } }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "license": "MIT" + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -3009,7 +3114,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", - "dev": true, "license": "MIT", "dependencies": { "@types/estree": "^1.0.6" @@ -3086,7 +3190,6 @@ "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -3117,7 +3220,7 @@ "version": "1.30.1", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", - "dev": true, + "devOptional": true, "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" @@ -3149,7 +3252,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3170,7 +3272,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3191,7 +3292,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3212,7 +3312,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3233,7 +3332,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3254,7 +3352,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3275,7 +3372,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3296,7 +3392,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3317,7 +3412,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3338,7 +3432,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3366,7 +3459,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", - "dev": true, "license": "MIT" }, "node_modules/locate-path": { @@ -3402,7 +3494,6 @@ "version": "0.30.17", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" @@ -3498,7 +3589,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -3508,7 +3598,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -3518,14 +3607,12 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, "funding": [ { "type": "github", @@ -3673,6 +3760,12 @@ "node": ">=8" } }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -3691,14 +3784,12 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -3723,7 +3814,6 @@ "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, "funding": [ { "type": "opencollective", @@ -4100,6 +4190,26 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -4114,7 +4224,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, + "devOptional": true, "license": "MIT", "funding": { "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" @@ -4143,7 +4253,6 @@ "version": "4.46.2", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz", "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==", - "dev": true, "license": "MIT", "dependencies": { "@types/estree": "1.0.8" @@ -4206,7 +4315,6 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", - "dev": true, "license": "MIT", "dependencies": { "mri": "^1.1.0" @@ -4232,7 +4340,6 @@ "version": "2.7.1", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", - "dev": true, "license": "MIT" }, "node_modules/shebang-command": { @@ -4268,7 +4375,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.1.tgz", "integrity": "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==", - "dev": true, "license": "MIT", "dependencies": { "@polka/url": "^1.0.0-next.24", @@ -4283,7 +4389,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -4315,11 +4420,22 @@ "node": ">=8" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/svelte": { "version": "5.38.1", "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.38.1.tgz", "integrity": "sha512-fO6CLDfJYWHgfo6lQwkQU2vhCiHc2MBl6s3vEhK+sSZru17YL4R5s1v14ndRpqKAIkq8nCz6MTk1yZbESZWeyQ==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.4", @@ -4440,7 +4556,6 @@ "version": "0.2.14", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", - "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.4.4", @@ -4469,7 +4584,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -4492,7 +4606,7 @@ "version": "4.20.4", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.4.tgz", "integrity": "sha512-yyxBKfORQ7LuRt/BQKBXrpcq59ZvSW0XxwfjAt3w2/8PmdxaFzijtMhTawprSHhpzeM5BgU2hXHG3lklIERZXg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "esbuild": "~0.25.0", @@ -4563,7 +4677,6 @@ "version": "7.10.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", - "dev": true, "license": "MIT", "optional": true, "peer": true @@ -4589,7 +4702,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.2.tgz", "integrity": "sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==", - "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", @@ -4664,7 +4776,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.1.tgz", "integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==", - "dev": true, "license": "MIT", "workspaces": [ "tests/deps/*", @@ -4824,7 +4935,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", - "dev": true, "license": "ISC", "optional": true, "peer": true, @@ -4852,7 +4962,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.2.tgz", "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==", - "dev": true, "license": "MIT" } } diff --git a/package.json b/package.json index b80394f..4d93fd5 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "dependencies": { "@prisma/client": "^6.14.0", "@prisma/extension-accelerate": "^2.0.2", + "@sveltejs/adapter-node": "^5.3.1", "cloudinary": "^2.7.0", "convert": "^5.12.0" } diff --git a/prisma/seed.ts b/prisma/seed.ts index f25b463..52a7332 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -47,7 +47,7 @@ async function main() { description: 'A rich and flavorful dressing made with vincotto, perfect for salads and marinades', time: 'Quick', station: 'Garde Manger', - instructions: '1. Combine all ingredients in a bowl\n2. Whisk until well combined\n3. Season to taste with salt and pepper\n4. Store in refrigerator for up to 1 week', + instructions: '', ingredients: { create: [ { ingredientId: ingredientMap.get('Vincotto')!.id, quantity: 90, unit: 'ml' }, @@ -68,7 +68,7 @@ async function main() { description: 'A bright and citrusy dressing with charred orange notes', time: 'Medium', station: 'Garde Manger', - instructions: '1. Char oranges over open flame until blackened\n2. Juice the charred oranges\n3. Combine all ingredients in a bowl\n4. Whisk until emulsified\n5. Season to taste', + instructions: '', ingredients: { create: [ { ingredientId: ingredientMap.get('Charred Orange')!.id, quantity: 5, unit: 'oranges' }, @@ -91,7 +91,7 @@ async function main() { description: 'A sweet and tangy vinegar solution for curing and marinating scallops', time: 'Quick', station: 'Garde Manger', - instructions: '1. Combine all ingredients in a saucepan\n2. Bring to a boil, stirring to dissolve sugar and salt\n3. Remove from heat and let cool\n4. Store in refrigerator for up to 2 weeks', + instructions: '', ingredients: { create: [ { ingredientId: ingredientMap.get('Water')!.id, quantity: 250, unit: 'ml' }, @@ -110,7 +110,7 @@ async function main() { description: 'A spicy and flavorful spread made with nduja sausage', time: 'Medium', station: 'Garde Manger', - instructions: '1. Soften butter to room temperature\n2. Combine all ingredients in a food processor\n3. Pulse until well combined but still chunky\n4. Season to taste\n5. Store in refrigerator for up to 1 week', + instructions: '', ingredients: { create: [ { ingredientId: ingredientMap.get('Nduja')!.id, quantity: 450, unit: 'g' }, diff --git a/server.mjs b/server.mjs new file mode 100644 index 0000000..1223b79 --- /dev/null +++ b/server.mjs @@ -0,0 +1,13 @@ +// ES module server wrapper that provides __dirname and __filename for Prisma +import { fileURLToPath } from 'url'; +import { dirname } from 'path'; + +// Provide __dirname and __filename for Prisma +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +global.__dirname = __dirname; +global.__filename = __filename; + +// Import and start the server +import('./build/index.js'); diff --git a/src/app.html b/src/app.html index 08c35c2..68bc724 100644 --- a/src/app.html +++ b/src/app.html @@ -3,7 +3,7 @@ - + %sveltekit.head% diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 4ff64a9..c7479ac 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -1,8 +1,9 @@ import type { Handle } from '@sveltejs/kit'; export const handle: Handle = async ({ event, resolve }) => { - // Check if user is authenticated using the new cookie API - const authenticated = event.cookies.get('chef_bible_auth') === 'authenticated'; + // Check if user is authenticated using the chef token cookie + const chefToken = event.cookies.get('chef_token'); + const authenticated = !!chefToken; // Add authentication status to locals for use in load functions event.locals.authenticated = authenticated; diff --git a/src/lib/utils/markdown.ts b/src/lib/utils/markdown.ts new file mode 100644 index 0000000..ad7482d --- /dev/null +++ b/src/lib/utils/markdown.ts @@ -0,0 +1,101 @@ +/** + * Simple markdown parser for basic formatting + * Supports: newlines, bold text (**text**), and links ([text](url)) + */ + +export interface MarkdownNode { + type: 'text' | 'bold' | 'link' | 'linebreak'; + content?: string; + url?: string; +} + +export function parseMarkdown(text: string): MarkdownNode[] { + if (!text) return []; + + const nodes: MarkdownNode[] = []; + let currentIndex = 0; + + while (currentIndex < text.length) { + // Check for line breaks first + if (text[currentIndex] === '\n') { + nodes.push({ type: 'linebreak' }); + currentIndex++; + continue; + } + + // Check for bold text (**text**) + if (text.slice(currentIndex, currentIndex + 2) === '**') { + const endIndex = text.indexOf('**', currentIndex + 2); + if (endIndex !== -1) { + const content = text.slice(currentIndex + 2, endIndex); + nodes.push({ type: 'bold', content }); + currentIndex = endIndex + 2; + continue; + } + } + + // Check for links ([text](url)) + if (text[currentIndex] === '[') { + const linkEnd = text.indexOf(']', currentIndex); + if (linkEnd !== -1 && text[linkEnd + 1] === '(') { + const urlStart = linkEnd + 2; + const urlEnd = text.indexOf(')', urlStart); + if (urlEnd !== -1) { + const linkText = text.slice(currentIndex + 1, linkEnd); + const url = text.slice(urlStart, urlEnd); + nodes.push({ type: 'link', content: linkText, url }); + currentIndex = urlEnd + 1; + continue; + } + } + } + + // Regular text - collect until we hit a special character + let textEnd = currentIndex; + while (textEnd < text.length && + text[textEnd] !== '\n' && + text[textEnd] !== '*' && + text[textEnd] !== '[') { + textEnd++; + } + + if (textEnd > currentIndex) { + const content = text.slice(currentIndex, textEnd); + nodes.push({ type: 'text', content }); + currentIndex = textEnd; + } else { + // Single character that doesn't match any pattern + nodes.push({ type: 'text', content: text[currentIndex] }); + currentIndex++; + } + } + + return nodes; +} + +export function renderMarkdownToHTML(text: string): string { + const nodes = parseMarkdown(text); + + return nodes.map(node => { + switch (node.type) { + case 'linebreak': + return '
'; + case 'bold': + return `${escapeHtml(node.content || '')}`; + case 'link': + return `${escapeHtml(node.content || '')}`; + case 'text': + default: + return escapeHtml(node.content || ''); + } + }).join(''); +} + +function escapeHtml(text: string): string { + return text + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 394df6c..3318848 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -47,19 +47,6 @@ © 2025 Birbante - Italian Apericena & Dolce Vita
- - - - - Chef Access - {#if data.authenticated} {/if} diff --git a/src/routes/chef-access/+page.server.ts b/src/routes/chef-access/+page.server.ts deleted file mode 100644 index 57fdc8d..0000000 --- a/src/routes/chef-access/+page.server.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { PageServerLoad } from './$types'; -import { validateMagicLinkToken } from '$lib/auth'; -import { redirect } from '@sveltejs/kit'; -import { MAGIC_LINK_TOKEN } from '$env/static/private'; -import { dev } from '$app/environment'; - -export const load: PageServerLoad = async ({ url, cookies }) => { - const token = url.searchParams.get('token'); - - // If no token provided, just show the page normally - if (!token) { - return { - authenticated: false, - message: 'No authentication token provided', - magicLinkToken: MAGIC_LINK_TOKEN - }; - } - - // Validate the token - if (validateMagicLinkToken(token, MAGIC_LINK_TOKEN)) { - // Set the authentication cookie using the new API - cookies.set('chef_bible_auth', 'authenticated', { - path: '/', - httpOnly: true, - sameSite: 'lax', // Changed from 'strict' to 'lax' for mobile Safari compatibility - secure: !dev, // Secure in production, not secure in development - maxAge: 30 * 24 * 60 * 60 // 30 days in seconds - }); - - // Redirect to home page with success message - throw redirect(302, '/?auth=success'); - } else { - // Invalid token - return { - authenticated: false, - message: 'Invalid authentication token', - magicLinkToken: MAGIC_LINK_TOKEN - }; - } -}; diff --git a/src/routes/chef-access/+page.svelte b/src/routes/chef-access/+page.svelte deleted file mode 100644 index 6018780..0000000 --- a/src/routes/chef-access/+page.svelte +++ /dev/null @@ -1,129 +0,0 @@ - - - - Chef Access - Chef Bible - - - -
-
-
-
-

Kitchen Staff Access

- -
-

- Scan the QR code below to get access to create and edit recipes. This access will last - for 30 days. -

- -
-
- -
-
-
📱
-
QR Code
-
Scan with your phone
-
-
-
-
- -
- - - - - - Magic Link: - {magicLink} - -
- -
-

What You Can Do With Access:

-
    -
  • - - - - Create new recipes with ingredients, instructions, and photos -
  • -
  • - - - - - Edit existing recipes to update ingredients or instructions -
  • -
  • - - - - Delete recipes when they're no longer needed -
  • -
  • - - - - Manage recipe details like cooking time and station -
  • -
-
-
-
-
-
-
diff --git a/src/routes/chef-cookie/+page.server.ts b/src/routes/chef-cookie/+page.server.ts index d1cd4b1..b97fdfc 100644 --- a/src/routes/chef-cookie/+page.server.ts +++ b/src/routes/chef-cookie/+page.server.ts @@ -20,8 +20,12 @@ export const load: PageServerLoad = async ({ url, cookies, setHeaders }) => { }); // Set a client-side cookie for UI state - setHeaders({ - 'set-cookie': `chef_authenticated=true; Path=/; Max-Age=${60 * 60 * 24 * 30}; SameSite=Lax` + cookies.set('chef_authenticated', 'true', { + path: '/', + httpOnly: false, + secure: process.env.NODE_ENV === 'production', + sameSite: 'lax', + maxAge: 60 * 60 * 24 * 30 // 30 days }); throw redirect(302, '/'); diff --git a/src/routes/health/+server.ts b/src/routes/health/+server.ts new file mode 100644 index 0000000..624852e --- /dev/null +++ b/src/routes/health/+server.ts @@ -0,0 +1,6 @@ +import type { RequestHandler } from './$types'; + +export const GET: RequestHandler = async () => { + return new Response('OK', { status: 200 }); +}; + diff --git a/src/routes/logout/+server.ts b/src/routes/logout/+server.ts index c431668..cbfed57 100644 --- a/src/routes/logout/+server.ts +++ b/src/routes/logout/+server.ts @@ -3,12 +3,20 @@ import { redirect } from '@sveltejs/kit'; import { dev } from '$app/environment'; export const POST: RequestHandler = async ({ cookies }) => { - // Clear the authentication cookie using the new API - cookies.set('chef_bible_auth', '', { + // Clear the chef authentication cookies + cookies.set('chef_token', '', { path: '/', httpOnly: true, - sameSite: 'lax', // Changed from 'strict' to 'lax' for mobile Safari compatibility - secure: !dev, // Secure in production, not secure in development + sameSite: 'lax', + secure: !dev, + maxAge: 0 // Expire immediately + }); + + cookies.set('chef_authenticated', '', { + path: '/', + httpOnly: false, + sameSite: 'lax', + secure: !dev, maxAge: 0 // Expire immediately }); @@ -17,12 +25,20 @@ export const POST: RequestHandler = async ({ cookies }) => { }; export const GET: RequestHandler = async ({ cookies }) => { - // Clear the authentication cookie using the new API - cookies.set('chef_bible_auth', '', { + // Clear the chef authentication cookies + cookies.set('chef_token', '', { path: '/', httpOnly: true, - sameSite: 'lax', // Changed from 'strict' to 'lax' for mobile Safari compatibility - secure: !dev, // Secure in production, not secure in development + sameSite: 'lax', + secure: !dev, + maxAge: 0 // Expire immediately + }); + + cookies.set('chef_authenticated', '', { + path: '/', + httpOnly: false, + sameSite: 'lax', + secure: !dev, maxAge: 0 // Expire immediately }); diff --git a/src/routes/recipe/[id]/+page.svelte b/src/routes/recipe/[id]/+page.svelte index df5927d..ab4a09d 100644 --- a/src/routes/recipe/[id]/+page.svelte +++ b/src/routes/recipe/[id]/+page.svelte @@ -1,6 +1,7 @@