This commit is contained in:
Benjamin Palko 2025-03-30 13:03:33 -04:00 committed by Benjamin Palko
commit bff3e89539
49 changed files with 2790 additions and 0 deletions

14
.editorconfig Normal file
View file

@ -0,0 +1,14 @@
root = true
[*]
end_of_line = lf
insert_final_newline = false
[*.{js,ts}]
indent_style = tab
indent_size = 4
[{*.{yml,mjs,json}}]
indent_style = space
indent_size = 2

27
.env Normal file
View file

@ -0,0 +1,27 @@
NPM_TOKEN=
##########################
# APP VARIABLES #
##########################
APP_NAME=chaos
##########################
# SECRETS #
##########################
SECRETS_PASSWORD=secret_do_not_commit_or_change_this_create_.env.local_instead
SECRETS_SALT=secret_do_not_commit_or_change_this_create_.env.local_instead
SECRETS_IV_POSITION=secret_do_not_commit_or_change_this_create_.env.local_instead
##########################
# DATABASE #
##########################
DB_USER=${APP_NAME}
DB_PASS=test123
DATABASE_URL="postgres://${APP_NAME}:${DB_PASS}@localhost:5432/${APP_NAME}"
DIRECT_URL="postgres://${APP_NAME}:${DB_PASS}@localhost:5432/${APP_NAME}"
##########################
# AUTHENTICATION #
##########################
PUBLIC_CLERK_PUBLISHABLE_KEY=secret_do_not_commit_or_change_this_create_.env.local_instead
CLERK_SECRET_KEY=secret_do_not_commit_or_change_this_create_.env.local_instead

46
.github/workflows/deploy.yml vendored Normal file
View file

@ -0,0 +1,46 @@
name: Deployment
on:
push:
branches:
- main
jobs:
deploy-app:
name: Deploy ${{ env.GITHUB_REPOSITORY }}
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
registry: ${{ env.GITHUB_SERVER_URL }}
username: ${{ env.GITHUB_REPOSITORY_OWNER }}
password: ${{ secrets.NPM_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v6
with:
push: true
tags: git.palko.ca/${{ env.GITHUB_REPOSITORY }}:latest
deploy-storybook:
name: Deploy ${{ env.GITHUB_REPOSITORY }}
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
registry: ${{ env.GITHUB_SERVER_URL }}
username: ${{ env.GITHUB_REPOSITORY_OWNER }}
password: ${{ secrets.NPM_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v6
with:
push: true
file: Dockerfile.storybook
tags: git.palko.ca/${{ env.GITHUB_REPOSITORY }}-storybook:latest

27
.github/workflows/pr.yml vendored Normal file
View file

@ -0,0 +1,27 @@
name: PR Gate
on:
pull_request:
branches: ['*']
jobs:
checks:
name: Checks
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Bun Setup
uses: oven-sh/setup-bun@v2
with:
bun-version-file: '.tool-versions'
- name: Install
run: bun install
- name: Lint
run: bun lint
- name: Build
run: bun run build
- name: Svelte Check
run: bun check
- name: Prisma Check
run: bun prisma:validate
- name: Test
run: bun run test:unit

28
.gitignore vendored Normal file
View file

@ -0,0 +1,28 @@
test-results
node_modules
# Output
.output
.vercel
.netlify
.wrangler
/.svelte-kit
/build
# OS
.DS_Store
Thumbs.db
.idea
# Storybook
storybook-static
# Vite
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
*/dev.db
*/dev.db-journal
# Env files
.env.*

6
.prettierignore Normal file
View file

@ -0,0 +1,6 @@
# Package Managers
package-lock.json
pnpm-lock.yaml
yarn.lock
*.md

21
.prettierrc Normal file
View file

@ -0,0 +1,21 @@
{
"useTabs": true,
"singleQuote": true,
"trailingComma": "es5",
"printWidth": 100,
"endOfLine": "lf",
"arrowParens": "always",
"jsxSingleQuote": false,
"semi": true,
"quoteProps": "as-needed",
"tabWidth": 4,
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
"overrides": [
{
"files": "*.svelte",
"options": {
"parser": "svelte"
}
}
]
}

19
.scripts/db.ts Normal file
View file

@ -0,0 +1,19 @@
import { exec } from 'child_process';
import { promisify } from 'util';
const appName = Bun.env.APP_NAME;
const execCommand = promisify(exec);
const command = `docker compose -p ${appName} -f devops/docker-compose.dev.yml up -d && docker compose -p ${appName} -f devops/docker-compose.dev.yml -f devops/docker-compose.wait.yml run --rm wait -c "${appName}-database:5432"`;
try {
const { stdout, stderr } = await execCommand(command, { env: Bun.env });
if (stderr) {
console.error(stderr);
}
console.log(stdout);
process.exit(0);
} catch (error) {
console.error(error);
process.exit(1);
}

17
.storybook/main.js Normal file
View file

@ -0,0 +1,17 @@
/** @type { import('@storybook/sveltekit').StorybookConfig } */
const config = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|ts|svelte)'],
addons: [
'@chromatic-com/storybook',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-styling-webpack',
'@storybook/addon-svelte-csf',
'@storybook/addon-themes',
],
framework: {
name: '@storybook/sveltekit',
options: {},
},
};
export default config;

3
.storybook/preview.css Normal file
View file

@ -0,0 +1,3 @@
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';

28
.storybook/preview.js Normal file
View file

@ -0,0 +1,28 @@
import { withThemeByDataAttribute } from '@storybook/addon-themes';
import './preview.css';
/** @type { import('@storybook/svelte').Preview } */
const preview = {
tags: ['autodocs'],
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
decorators: [
withThemeByDataAttribute({
themes: {
light: 'light',
dark: 'dark',
night: 'night',
},
defaultTheme: 'dark',
attributeName: 'data-theme',
}),
],
};
export default preview;

1
.tool-versions Normal file
View file

@ -0,0 +1 @@
bun 1.2.5

17
Dockerfile Normal file
View file

@ -0,0 +1,17 @@
FROM oven/bun:1.2-alpine AS build
WORKDIR /opt/build
COPY . .
RUN bun install --frozen-lockfile \
&& bun run build
FROM node:18-alpine3.21
WORKDIR /opt/app
COPY ./package.json ./package.json
COPY --from=build --chown=1000:1000 /opt/build/node_modules/ /opt/app/node_modules/
COPY --from=build --chown=1000:1000 /opt/build/build /opt/app
ENTRYPOINT [ "node", "index.js" ]

11
Dockerfile.storybook Normal file
View file

@ -0,0 +1,11 @@
FROM oven/bun:1.2-alpine AS build
WORKDIR /opt/build
COPY . .
RUN bun install --frozen-lockfile \
&& bun run build-storybook
FROM nginx:alpine3.21
COPY --from=build --chown=1000:1000 /opt/build/storybook-static /usr/share/nginx/html

9
LICENSE Normal file
View file

@ -0,0 +1,9 @@
MIT License
Copyright (c) 2025 pantheon
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

2
README.md Normal file
View file

@ -0,0 +1,2 @@
# svelte-template

1959
bun.lock Normal file

File diff suppressed because it is too large Load diff

3
bunfig.toml Normal file
View file

@ -0,0 +1,3 @@
[install.scopes]
# registry with token
"@pantheon" = { token = "$NPM_TOKEN", url = "https://git.palko.ca/api/packages/pantheon/npm/" }

View file

@ -0,0 +1,10 @@
name: ${APP_NAME:?error}
services:
database:
hostname: '${APP_NAME:?error}-database'
image: 'postgres:12-alpine'
ports:
- '5432:5432'
environment:
POSTGRES_USER: ${DB_USER:?error}
POSTGRES_PASSWORD: ${DB_PASS:?error}

View file

@ -0,0 +1,3 @@
services:
wait:
image: dokku/wait

0
e2e/demo.test.ts Normal file
View file

55
eslint.config.js Normal file
View file

@ -0,0 +1,55 @@
import prettier from 'eslint-config-prettier';
import js from '@eslint/js';
import { includeIgnoreFile } from '@eslint/compat';
import svelte from 'eslint-plugin-svelte';
import globals from 'globals';
import { fileURLToPath } from 'node:url';
import ts from 'typescript-eslint';
const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url));
export default ts.config(
includeIgnoreFile(gitignorePath),
js.configs.recommended,
...ts.configs.recommended,
...svelte.configs['flat/recommended'],
prettier,
...svelte.configs['flat/prettier'],
{
languageOptions: {
globals: {
...globals.browser,
...globals.node,
},
},
},
{
files: ['**/*.svelte'],
languageOptions: {
parserOptions: {
parser: ts.parser,
},
},
},
{
rules: {
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
},
],
'no-restricted-syntax': [
'error',
{
selector:
'CallExpression:matches([callee.object.object.name="prisma"], [callee.object.object.name="prismaTransactionClient"], [callee.object.object.name="transactionClient"]):matches([callee.property.name="findFirst"], [callee.property.name="findMany"], [callee.property.name="updateMany"], [callee.property.name="deleteMany"], [callee.property.name="count"], [callee.property.name="aggregate"], [callee.property.name="groupBy"]):not(:has(ObjectExpression > Property[key.name="where"] > ObjectExpression > Property[key.name="tenantId"]))',
message:
'Please filter on the current tenant when using findFirst, findMany, updateMany, deleteMany, count, aggregate or groupBy.',
},
],
},
}
);

3
messages/en.json Normal file
View file

@ -0,0 +1,3 @@
{
"$schema": "https://inlang.com/schema/inlang-message-format"
}

98
package.json Normal file
View file

@ -0,0 +1,98 @@
{
"name": "chaos",
"version": "0.0.1",
"type": "module",
"scripts": {
"postinstall": "prisma generate",
"dev": "bun database:up && bun prisma:generate && vite dev",
"build": "vite build",
"build-storybook": "storybook build",
"database:up": "bun .scripts/db.ts && bun prisma:push",
"database:down": "docker compose -p hestia -f devops/docker-compose.dev.yml down",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"format": "prettier --write .",
"generate-secret": "bun ./scripts/generate-secret.ts",
"lint": "prettier --check . && eslint .",
"test:unit": "vitest",
"test": "bun run test:unit -- --run && bun run test:e2e",
"storybook": "storybook dev -p 6006",
"test:e2e": "playwright test",
"prisma:dev": "prisma migrate dev",
"prisma:format": "prisma format",
"prisma:generate": "prisma generate",
"prisma:push": "prisma db push",
"prisma:reset": "prisma migrate reset --force",
"prisma:studio": "prisma studio",
"prisma:validate": "prisma validate",
"prepare": "husky"
},
"devDependencies": {
"@chromatic-com/storybook": "^3.2.2",
"@eslint/compat": "^1.2.3",
"@playwright/test": "^1.45.3",
"@storybook/addon-essentials": "^8.5.0",
"@storybook/addon-interactions": "^8.5.0",
"@storybook/addon-styling-webpack": "^1.0.1",
"@storybook/addon-svelte-csf": "^5.0.0-next.13",
"@storybook/addon-themes": "^8.5.0",
"@storybook/blocks": "^8.5.0",
"@storybook/svelte": "^8.5.0",
"@storybook/sveltekit": "^8.5.0",
"@storybook/test": "^8.5.0",
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/kit": "^2.15.3",
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"@types/bun": "^1.1.15",
"autoprefixer": "^10.4.20",
"daisyui": "^4.12.22",
"eslint": "^9.7.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.36.0",
"globals": "^15.0.0",
"husky": "^9.1.7",
"lint-staged": "^15.3.0",
"prettier": "^3.3.2",
"prettier-plugin-svelte": "^3.2.6",
"prettier-plugin-tailwindcss": "^0.6.5",
"prisma": "6.3.1",
"storybook": "^8.5.0",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"tailwindcss": "^3.4.9",
"typescript": "^5.0.0",
"typescript-eslint": "^8.0.0",
"vite": "^6.0.0",
"vitest": "^2.0.4"
},
"dependencies": {
"@clerk/backend": "1.21.4",
"@clerk/themes": "^2.2.3",
"@inlang/paraglide-sveltekit": "^0.15.0",
"@pantheon/theia": "^1.0.0",
"@pothos/core": "^4.3.0",
"@pothos/plugin-prisma": "^4.4.0",
"@prisma/client": "6.3.1",
"@tailwindcss/typography": "^0.5.15",
"clerk-sveltekit": "https://pkg.pr.new/wobsoriano/clerk-sveltekit@ca15d4e",
"clsx": "^2.1.1",
"dayjs": "^1.11.13",
"eslint_d": "^14.3.0",
"graphql": "^16.9.0",
"graphql-yoga": "^5.10.4",
"lucide-svelte": "^0.469.0",
"pino": "^9.5.0",
"pino-pretty": "^13.0.0",
"tailwind-merge": "^2.5.5",
"twilio": "^5.4.0",
"zod": "^3.24.0"
},
"lint-staged": {
"*.{json,yml,yaml,css}": "prettier --write",
"*.{js,ts,svelte}": [
"prettier --write",
"eslint --fix"
]
}
}

0
playwright.config.ts Normal file
View file

6
postcss.config.js Normal file
View file

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

13
prisma/schema.prisma Normal file
View file

@ -0,0 +1,13 @@
generator client {
provider = "prisma-client-js"
}
generator pothos {
provider = "prisma-pothos-types"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
directUrl = env("DIRECT_URL")
}

1
project.inlang/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
cache

View file

@ -0,0 +1 @@
0562ab5d57c0427c0e2a28e7393a4cb0f7bf5e59a8474a5f59ebaabdda3caf02

View file

@ -0,0 +1,17 @@
{
"$schema": "https://inlang.com/schema/project-settings",
"modules": [
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@1/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-identical-pattern@1/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-missing-translation@1/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-without-source@1/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-valid-js-identifier@1/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@2/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@0/dist/index.js"
],
"plugin.inlang.messageFormat": {
"pathPattern": "./messages/{languageTag}.json"
},
"locales": ["en"],
"baseLocale": "en"
}

7
src/app.css Normal file
View file

@ -0,0 +1,7 @@
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
:root {
@apply text-base-content;
}

24
src/app.d.ts vendored Normal file
View file

@ -0,0 +1,24 @@
// See https://svelte.dev/docs/kit/types#app.d.ts
import type { Tenant, User } from '@prisma/client';
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
interface Locals {
auth: {
userId?: string;
orgId?: string | null;
sessionId?: string;
};
user: User;
tenant: Tenant;
}
// interface PageData {}
// interface PageState {}
// interface Platform {}
}
}
export {};

12
src/app.html Normal file
View file

@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

7
src/demo.spec.ts Normal file
View file

@ -0,0 +1,7 @@
import { describe, it, expect } from 'vitest';
describe('sum test', () => {
it('adds 1 + 2 to equal 3', () => {
expect(1 + 2).toBe(3);
});
});

5
src/hooks.server.ts Normal file
View file

@ -0,0 +1,5 @@
import { validateSession } from '$lib/server/middleware';
import { sequence } from '@sveltejs/kit/hooks';
import { withClerkHandler } from 'clerk-sveltekit/server';
export const handle = sequence(withClerkHandler(), validateSession());

4
src/lib/i18n/index.ts Normal file
View file

@ -0,0 +1,4 @@
import * as runtime from '$lib/paraglide/runtime';
import { createI18n } from '@inlang/paraglide-sveltekit';
export * as messages from '$lib/paraglide/messages';
export const i18n = createI18n(runtime);

0
src/lib/index.ts Normal file
View file

View file

@ -0,0 +1,34 @@
import { NODE_ENV } from '$env/static/private';
import { type YogaLogger } from 'graphql-yoga';
import pino from 'pino';
export const logger = pino({
// Only use pino-pretty when NOT production
...(NODE_ENV !== 'production' && {
transport: {
target: 'pino-pretty',
options: {
colorize: true,
},
},
}),
});
export const yogaLogger: YogaLogger = {
debug(...args) {
// @ts-expect-error types dont match
logger.debug(...args);
},
info(...args) {
// @ts-expect-error types dont match
logger.info(...args);
},
warn(...args) {
// @ts-expect-error types dont match
logger.warn(...args);
},
error(...args) {
// @ts-expect-error types dont match
logger.error(...args);
},
};

View file

@ -0,0 +1 @@
export * from './validateSesssion';

View file

@ -0,0 +1,105 @@
import { logger } from '$lib/server/logger';
import { prisma } from '$lib/server/prisma';
import { type Handle } from '@sveltejs/kit';
import { clerkClient } from 'clerk-sveltekit/server';
const publicRoutes = ['/login'];
const loginRedirect = () =>
new Response(null, {
status: 307,
headers: {
location: '/login',
},
});
async function findOrCreateTenant(tenantClerkId: string) {
const tenant = await prisma.tenant.findUnique({
where: {
clerkOrganizationId: tenantClerkId,
},
});
if (tenant) {
return tenant;
}
const organization = await clerkClient.organizations.getOrganization({
organizationId: tenantClerkId,
});
return await prisma.tenant.create({
data: {
clerkOrganizationId: tenantClerkId,
name: organization.name,
slug: organization.slug ?? `tenant-${tenantClerkId}`,
},
});
}
export function validateSession(): Handle {
return async ({ event: { locals, url, ...rest }, resolve }) => {
// Public route? LET THEM PASS!
if (url !== null && publicRoutes.includes(url.pathname)) {
return resolve({ locals, url, ...rest });
}
// No session, redirect!
if (!locals.auth.sessionId) {
return loginRedirect();
}
// No user, revoke session and redirect!
if (!locals.auth.userId) {
await clerkClient.sessions.revokeSession(locals.auth.sessionId);
return loginRedirect();
}
// No org, signout and redirect!
if (!locals.auth.orgId) {
await clerkClient.sessions.revokeSession(locals.auth.sessionId);
return loginRedirect();
}
// Make sure that a tenant exists for the clerk org
const tenant = await findOrCreateTenant(locals.auth.orgId);
// Make sure a user exists for the clerk user
const clerkUser = await clerkClient.users.getUser(locals.auth.userId);
let user = await prisma.user.findFirst({
where: {
clerkId: clerkUser.id,
tenantId: tenant.id,
},
});
if (!user) {
if (clerkUser.emailAddresses.length === 0) {
logger.error('User has no email address');
await clerkClient.sessions.revokeSession(locals.auth.sessionId);
return loginRedirect();
}
user = await prisma.user.create({
data: {
clerkId: clerkUser.id,
email: clerkUser.emailAddresses[0].emailAddress,
name: clerkUser.fullName ?? '',
tenantId: tenant.id,
},
});
if (clerkUser.fullName === null) {
logger.warn(`User {${user.id}} has no name!`);
}
}
// Load user and tenant into locals
locals.user = user;
locals.tenant = tenant;
return resolve({ locals, url, ...rest });
};
}

View file

@ -0,0 +1,17 @@
import { PrismaClient } from '@prisma/client';
import { logger } from '../logger';
export const prisma = new PrismaClient({
log: [
{ emit: 'event', level: 'query' },
{ emit: 'event', level: 'info' },
],
});
prisma.$on('query', (event) => {
logger.debug(`Query [${event.duration}ms]: ${event.query}`);
});
prisma.$on('info', (event) => {
logger.info(event.message);
});

20
src/routes/+error.svelte Normal file
View file

@ -0,0 +1,20 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { page } from '$app/state';
import { messages } from '$lib/i18n';
</script>
<main class="flex h-full w-full flex-col items-center justify-center gap-8">
<div class="flex items-center gap-2">
<h1 class="text-5xl font-bold tracking-widest text-white">
{page.status}
</h1>
<div class="divider divider-horizontal"></div>
<h1 class="text-2xl font-bold">
{page.error?.message}
</h1>
</div>
<button onclick={() => goto('/')} class="btn btn-outline btn-neutral btn-wide"
>{messages.error_page_go_home()}</button
>
</main>

23
src/routes/+layout.svelte Normal file
View file

@ -0,0 +1,23 @@
<script lang="ts">
import ClerkProvider from 'clerk-sveltekit/client/ClerkProvider.svelte';
import { i18n } from '$lib/i18n';
import { ParaglideJS } from '@inlang/paraglide-sveltekit';
import '../app.css';
import { PUBLIC_CLERK_PUBLISHABLE_KEY } from '$env/static/public';
let { children } = $props();
</script>
<ParaglideJS {i18n}>
<ClerkProvider publishableKey={PUBLIC_CLERK_PUBLISHABLE_KEY}>
<div class="layout">
{@render children()}
</div>
</ClerkProvider>
</ParaglideJS>
<style>
.layout {
@apply h-screen w-screen p-8;
}
</style>

0
src/routes/+page.svelte Normal file
View file

BIN
static/favicon.png Normal file

Binary file not shown.

21
svelte.config.js Normal file
View file

@ -0,0 +1,21 @@
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://svelte.dev/docs/kit/integrations
// for more information about preprocessors
preprocess: vitePreprocess(),
kit: {
version: {
name: '1.0.0-alpha',
},
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
adapter: adapter(),
},
};
export default config;

29
tailwind.config.ts Normal file
View file

@ -0,0 +1,29 @@
import typography from '@tailwindcss/typography';
import daisyui from 'daisyui';
import type { Config } from 'tailwindcss';
export default {
content: ['./src/**/*.{html,js,svelte,ts}'],
theme: {
extend: {
fontFamily: {
display: ['Baskervville SC'],
},
animation: {
fade: 'fadeIn .5s ease-in-out',
},
keyframes: {
fadeIn: {
from: { opacity: '0' },
to: { opacity: '1' },
},
},
},
},
plugins: [typography, daisyui],
daisyui: {
logs: false,
},
} satisfies Config;

19
tsconfig.json Normal file
View file

@ -0,0 +1,19 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler"
}
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
}

17
vite.config.ts Normal file
View file

@ -0,0 +1,17 @@
import { defineConfig } from 'vitest/config';
import { paraglide } from '@inlang/paraglide-sveltekit/vite';
import { sveltekit } from '@sveltejs/kit/vite';
export default defineConfig({
plugins: [
sveltekit(),
paraglide({
project: './project.inlang',
outdir: './src/lib/paraglide',
}),
],
test: {
include: ['src/**/*.{test,spec}.{js,ts}'],
},
});