├── .gitignore ├── .svelte-kit ├── ambient.d.ts ├── generated │ ├── client-manifest.js │ ├── client-matchers.js │ ├── client │ │ ├── app.js │ │ ├── matchers.js │ │ └── nodes │ │ │ ├── 0.js │ │ │ ├── 1.js │ │ │ ├── 2.js │ │ │ ├── 3.js │ │ │ ├── 4.js │ │ │ ├── 5.js │ │ │ ├── 6.js │ │ │ ├── 7.js │ │ │ └── 8.js │ ├── nodes │ │ ├── 0.js │ │ ├── 1.js │ │ └── 2.js │ ├── root.svelte │ └── server │ │ └── internal.js ├── tsconfig.json └── types │ ├── route_meta_data.json │ └── src │ └── routes │ ├── $types.d.ts │ ├── (iframe) │ └── demo │ │ ├── $types.d.ts │ │ └── [row_id] │ │ ├── $types.d.ts │ │ └── [mode] │ │ └── $types.d.ts │ └── (main) │ ├── $types.d.ts │ ├── contact │ └── $types.d.ts │ └── depth │ └── $types.d.ts ├── Dockerfile ├── LICENSE ├── README.md ├── docker-compose.yml ├── header.png ├── jsconfig.json ├── package-lock.json ├── package.json ├── src ├── app.html ├── components │ ├── Button.svelte │ ├── CodeSnippet.svelte │ ├── CopyInput.svelte │ ├── DataBall.svelte │ ├── DataTable.svelte │ ├── DemoBox.svelte │ ├── FeatureCard.svelte │ ├── FeatureItem.svelte │ ├── Frame.svelte │ ├── Input.svelte │ ├── Markup.svelte │ ├── QRCode.svelte │ ├── ShowOff.svelte │ ├── Subtitle.svelte │ └── Title.svelte ├── data │ └── code.json ├── global.d.ts ├── routes │ ├── (iframe) │ │ ├── datatable │ │ │ ├── +page.js │ │ │ └── +page.svelte │ │ └── demo │ │ │ ├── +layout.svelte │ │ │ └── [row_id] │ │ │ ├── +page.js │ │ │ ├── +page.svelte │ │ │ └── [mode] │ │ │ ├── +page.js │ │ │ └── +page.svelte │ ├── (main) │ │ ├── +layout.svelte │ │ ├── +page.js │ │ ├── +page.svelte │ │ ├── contact │ │ │ ├── +page.js │ │ │ └── +page.svelte │ │ └── depth │ │ │ ├── +page.js │ │ │ └── +page.svelte │ └── +error.svelte └── utils │ ├── condition.ts │ ├── mapStore.ts │ ├── primaryKeys.ts │ ├── svupa.ts │ ├── table.ts │ ├── tableRow.ts │ └── tableStore.ts ├── static ├── arrow.png ├── arrow2.png ├── check.png ├── concept.png ├── copy.png ├── demo_hero.gif ├── frameworks.png ├── github.png ├── icon.png ├── link.png ├── plus.png ├── qr.png ├── styles.css ├── tail.css ├── trash.png └── x.png ├── svelte.config.js ├── tailwind.config.cjs └── vite.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /.svelte-kit/ambient.d.ts: -------------------------------------------------------------------------------- 1 | 2 | // this file is generated — do not edit it 3 | 4 | 5 | /// 6 | 7 | /** 8 | * Environment variables [loaded by Vite](https://vitejs.dev/guide/env-and-mode.html#env-files) from `.env` files and `process.env`. Like [`$env/dynamic/private`](https://kit.svelte.dev/docs/modules#$env-dynamic-private), this module cannot be imported into client-side code. This module only includes variables that _do not_ begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#env) _and do_ start with [`config.kit.env.privatePrefix`](https://kit.svelte.dev/docs/configuration#env) (if configured). 9 | * 10 | * _Unlike_ [`$env/dynamic/private`](https://kit.svelte.dev/docs/modules#$env-dynamic-private), the values exported from this module are statically injected into your bundle at build time, enabling optimisations like dead code elimination. 11 | * 12 | * ```ts 13 | * import { API_KEY } from '$env/static/private'; 14 | * ``` 15 | * 16 | * Note that all environment variables referenced in your code should be declared (for example in an `.env` file), even if they don't have a value until the app is deployed: 17 | * 18 | * ``` 19 | * MY_FEATURE_FLAG="" 20 | * ``` 21 | * 22 | * You can override `.env` values from the command line like so: 23 | * 24 | * ```bash 25 | * MY_FEATURE_FLAG="enabled" npm run dev 26 | * ``` 27 | */ 28 | declare module '$env/static/private' { 29 | export const MANPATH: string; 30 | export const TERM_PROGRAM: string; 31 | export const NODE: string; 32 | export const INIT_CWD: string; 33 | export const TERM: string; 34 | export const SHELL: string; 35 | export const npm_config_metrics_registry: string; 36 | export const HOMEBREW_REPOSITORY: string; 37 | export const TMPDIR: string; 38 | export const npm_config_global_prefix: string; 39 | export const TERM_PROGRAM_VERSION: string; 40 | export const npm_package_optional: string; 41 | export const COLOR: string; 42 | export const npm_config_noproxy: string; 43 | export const npm_config_local_prefix: string; 44 | export const USER: string; 45 | export const COMMAND_MODE: string; 46 | export const npm_config_globalconfig: string; 47 | export const npm_package_peer: string; 48 | export const SSH_AUTH_SOCK: string; 49 | export const __CF_USER_TEXT_ENCODING: string; 50 | export const WARP_IS_LOCAL_SHELL_SESSION: string; 51 | export const npm_execpath: string; 52 | export const WARP_USE_SSH_WRAPPER: string; 53 | export const npm_package_integrity: string; 54 | export const PATH: string; 55 | export const npm_package_json: string; 56 | export const _: string; 57 | export const LaunchInstanceID: string; 58 | export const npm_config_userconfig: string; 59 | export const npm_config_init_module: string; 60 | export const __CFBundleIdentifier: string; 61 | export const npm_command: string; 62 | export const PWD: string; 63 | export const npm_lifecycle_event: string; 64 | export const EDITOR: string; 65 | export const npm_package_name: string; 66 | export const XPC_FLAGS: string; 67 | export const npm_package_engines_node: string; 68 | export const npm_config_node_gyp: string; 69 | export const npm_package_dev: string; 70 | export const npm_package_version: string; 71 | export const XPC_SERVICE_NAME: string; 72 | export const npm_package_resolved: string; 73 | export const SHLVL: string; 74 | export const HOME: string; 75 | export const HOMEBREW_PREFIX: string; 76 | export const npm_package_dev_optional: string; 77 | export const npm_config_cache: string; 78 | export const LOGNAME: string; 79 | export const npm_lifecycle_script: string; 80 | export const LC_CTYPE: string; 81 | export const SSH_SOCKET_DIR: string; 82 | export const npm_config_user_agent: string; 83 | export const INFOPATH: string; 84 | export const HOMEBREW_CELLAR: string; 85 | export const CONDA_CHANGEPS1: string; 86 | export const SECURITYSESSIONID: string; 87 | export const npm_node_execpath: string; 88 | export const npm_config_prefix: string; 89 | export const COLORTERM: string; 90 | } 91 | 92 | /** 93 | * Similar to [`$env/static/private`](https://kit.svelte.dev/docs/modules#$env-static-private), except that it only includes environment variables that begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#env) (which defaults to `PUBLIC_`), and can therefore safely be exposed to client-side code. 94 | * 95 | * Values are replaced statically at build time. 96 | * 97 | * ```ts 98 | * import { PUBLIC_BASE_URL } from '$env/static/public'; 99 | * ``` 100 | */ 101 | declare module '$env/static/public' { 102 | 103 | } 104 | 105 | /** 106 | * This module provides access to runtime environment variables, as defined by the platform you're running on. For example if you're using [`adapter-node`](https://github.com/sveltejs/kit/tree/master/packages/adapter-node) (or running [`vite preview`](https://kit.svelte.dev/docs/cli)), this is equivalent to `process.env`. This module only includes variables that _do not_ begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#env) _and do_ start with [`config.kit.env.privatePrefix`](https://kit.svelte.dev/docs/configuration#env) (if configured). 107 | * 108 | * This module cannot be imported into client-side code. 109 | * 110 | * ```ts 111 | * import { env } from '$env/dynamic/private'; 112 | * console.log(env.DEPLOYMENT_SPECIFIC_VARIABLE); 113 | * ``` 114 | * 115 | * > In `dev`, `$env/dynamic` always includes environment variables from `.env`. In `prod`, this behavior will depend on your adapter. 116 | */ 117 | declare module '$env/dynamic/private' { 118 | export const env: { 119 | MANPATH: string; 120 | TERM_PROGRAM: string; 121 | NODE: string; 122 | INIT_CWD: string; 123 | TERM: string; 124 | SHELL: string; 125 | npm_config_metrics_registry: string; 126 | HOMEBREW_REPOSITORY: string; 127 | TMPDIR: string; 128 | npm_config_global_prefix: string; 129 | TERM_PROGRAM_VERSION: string; 130 | npm_package_optional: string; 131 | COLOR: string; 132 | npm_config_noproxy: string; 133 | npm_config_local_prefix: string; 134 | USER: string; 135 | COMMAND_MODE: string; 136 | npm_config_globalconfig: string; 137 | npm_package_peer: string; 138 | SSH_AUTH_SOCK: string; 139 | __CF_USER_TEXT_ENCODING: string; 140 | WARP_IS_LOCAL_SHELL_SESSION: string; 141 | npm_execpath: string; 142 | WARP_USE_SSH_WRAPPER: string; 143 | npm_package_integrity: string; 144 | PATH: string; 145 | npm_package_json: string; 146 | _: string; 147 | LaunchInstanceID: string; 148 | npm_config_userconfig: string; 149 | npm_config_init_module: string; 150 | __CFBundleIdentifier: string; 151 | npm_command: string; 152 | PWD: string; 153 | npm_lifecycle_event: string; 154 | EDITOR: string; 155 | npm_package_name: string; 156 | XPC_FLAGS: string; 157 | npm_package_engines_node: string; 158 | npm_config_node_gyp: string; 159 | npm_package_dev: string; 160 | npm_package_version: string; 161 | XPC_SERVICE_NAME: string; 162 | npm_package_resolved: string; 163 | SHLVL: string; 164 | HOME: string; 165 | HOMEBREW_PREFIX: string; 166 | npm_package_dev_optional: string; 167 | npm_config_cache: string; 168 | LOGNAME: string; 169 | npm_lifecycle_script: string; 170 | LC_CTYPE: string; 171 | SSH_SOCKET_DIR: string; 172 | npm_config_user_agent: string; 173 | INFOPATH: string; 174 | HOMEBREW_CELLAR: string; 175 | CONDA_CHANGEPS1: string; 176 | SECURITYSESSIONID: string; 177 | npm_node_execpath: string; 178 | npm_config_prefix: string; 179 | COLORTERM: string; 180 | [key: `PUBLIC_${string}`]: undefined; 181 | [key: `${string}`]: string | undefined; 182 | } 183 | } 184 | 185 | /** 186 | * Similar to [`$env/dynamic/private`](https://kit.svelte.dev/docs/modules#$env-dynamic-private), but only includes variables that begin with [`config.kit.env.publicPrefix`](https://kit.svelte.dev/docs/configuration#env) (which defaults to `PUBLIC_`), and can therefore safely be exposed to client-side code. 187 | * 188 | * Note that public dynamic environment variables must all be sent from the server to the client, causing larger network requests — when possible, use `$env/static/public` instead. 189 | * 190 | * ```ts 191 | * import { env } from '$env/dynamic/public'; 192 | * console.log(env.PUBLIC_DEPLOYMENT_SPECIFIC_VARIABLE); 193 | * ``` 194 | */ 195 | declare module '$env/dynamic/public' { 196 | export const env: { 197 | [key: `PUBLIC_${string}`]: string | undefined; 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /.svelte-kit/generated/client-manifest.js: -------------------------------------------------------------------------------- 1 | export { matchers } from './client-matchers.js'; 2 | 3 | export const nodes = [() => import('./nodes/0'), 4 | () => import('./nodes/1'), 5 | () => import('./nodes/2')]; 6 | 7 | export const server_loads = []; 8 | 9 | export const dictionary = { 10 | "/": [2] 11 | }; 12 | 13 | export const hooks = { 14 | handleError: (({ error }) => { console.error(error) }), 15 | }; -------------------------------------------------------------------------------- /.svelte-kit/generated/client-matchers.js: -------------------------------------------------------------------------------- 1 | export const matchers = {}; -------------------------------------------------------------------------------- /.svelte-kit/generated/client/app.js: -------------------------------------------------------------------------------- 1 | export { matchers } from './matchers.js'; 2 | 3 | export const nodes = [ 4 | () => import('./nodes/0'), 5 | () => import('./nodes/1'), 6 | () => import('./nodes/2'), 7 | () => import('./nodes/3'), 8 | () => import('./nodes/4'), 9 | () => import('./nodes/5'), 10 | () => import('./nodes/6'), 11 | () => import('./nodes/7'), 12 | () => import('./nodes/8') 13 | ]; 14 | 15 | export const server_loads = []; 16 | 17 | export const dictionary = { 18 | "/(main)": [6,[3]], 19 | "/(main)/contact": [7,[3]], 20 | "/(iframe)/demo/[row_id]": [4,[2]], 21 | "/(iframe)/demo/[row_id]/[mode]": [5,[2]], 22 | "/(main)/depth": [8,[3]] 23 | }; 24 | 25 | export const hooks = { 26 | handleError: (({ error }) => { console.error(error) }), 27 | }; 28 | 29 | export { default as root } from '../root.svelte'; -------------------------------------------------------------------------------- /.svelte-kit/generated/client/matchers.js: -------------------------------------------------------------------------------- 1 | export const matchers = {}; -------------------------------------------------------------------------------- /.svelte-kit/generated/client/nodes/0.js: -------------------------------------------------------------------------------- 1 | export { default as component } from "../../../../node_modules/@sveltejs/kit/src/runtime/components/layout.svelte"; -------------------------------------------------------------------------------- /.svelte-kit/generated/client/nodes/1.js: -------------------------------------------------------------------------------- 1 | export { default as component } from "../../../../src/routes/+error.svelte"; -------------------------------------------------------------------------------- /.svelte-kit/generated/client/nodes/2.js: -------------------------------------------------------------------------------- 1 | export { default as component } from "../../../../src/routes/(iframe)/demo/+layout.svelte"; -------------------------------------------------------------------------------- /.svelte-kit/generated/client/nodes/3.js: -------------------------------------------------------------------------------- 1 | export { default as component } from "../../../../src/routes/(main)/+layout.svelte"; -------------------------------------------------------------------------------- /.svelte-kit/generated/client/nodes/4.js: -------------------------------------------------------------------------------- 1 | import * as universal from "../../../../src/routes/(iframe)/demo/[row_id]/+page.js"; 2 | export { universal }; 3 | export { default as component } from "../../../../src/routes/(iframe)/demo/[row_id]/+page.svelte"; -------------------------------------------------------------------------------- /.svelte-kit/generated/client/nodes/5.js: -------------------------------------------------------------------------------- 1 | import * as universal from "../../../../src/routes/(iframe)/demo/[row_id]/[mode]/+page.js"; 2 | export { universal }; 3 | export { default as component } from "../../../../src/routes/(iframe)/demo/[row_id]/[mode]/+page.svelte"; -------------------------------------------------------------------------------- /.svelte-kit/generated/client/nodes/6.js: -------------------------------------------------------------------------------- 1 | import * as universal from "../../../../src/routes/(main)/+page.js"; 2 | export { universal }; 3 | export { default as component } from "../../../../src/routes/(main)/+page.svelte"; -------------------------------------------------------------------------------- /.svelte-kit/generated/client/nodes/7.js: -------------------------------------------------------------------------------- 1 | import * as universal from "../../../../src/routes/(main)/contact/+page.js"; 2 | export { universal }; 3 | export { default as component } from "../../../../src/routes/(main)/contact/+page.svelte"; -------------------------------------------------------------------------------- /.svelte-kit/generated/client/nodes/8.js: -------------------------------------------------------------------------------- 1 | import * as universal from "../../../../src/routes/(main)/depth/+page.js"; 2 | export { universal }; 3 | export { default as component } from "../../../../src/routes/(main)/depth/+page.svelte"; -------------------------------------------------------------------------------- /.svelte-kit/generated/nodes/0.js: -------------------------------------------------------------------------------- 1 | export { default as component } from "../../../src/routes/+layout.svelte"; -------------------------------------------------------------------------------- /.svelte-kit/generated/nodes/1.js: -------------------------------------------------------------------------------- 1 | export { default as component } from "../../../src/routes/+error.svelte"; -------------------------------------------------------------------------------- /.svelte-kit/generated/nodes/2.js: -------------------------------------------------------------------------------- 1 | import * as universal from "../../../src/routes/+page.js"; 2 | export { universal }; 3 | export { default as component } from "../../../src/routes/+page.svelte"; -------------------------------------------------------------------------------- /.svelte-kit/generated/root.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 43 | 44 | {#if constructors[1]} 45 | 46 | {#if constructors[2]} 47 | 48 | 49 | 50 | {:else} 51 | 52 | {/if} 53 | 54 | {:else} 55 | 56 | {/if} 57 | 58 | {#if mounted} 59 |
60 | {#if navigated} 61 | {title} 62 | {/if} 63 |
64 | {/if} -------------------------------------------------------------------------------- /.svelte-kit/generated/server/internal.js: -------------------------------------------------------------------------------- 1 | 2 | import root from '../root.svelte'; 3 | import { set_building } from '__sveltekit/environment'; 4 | import { set_assets } from '__sveltekit/paths'; 5 | import { set_private_env, set_public_env } from '../../../node_modules/@sveltejs/kit/src/runtime/shared-server.js'; 6 | 7 | export const options = { 8 | app_template_contains_nonce: false, 9 | csp: {"mode":"auto","directives":{"upgrade-insecure-requests":false,"block-all-mixed-content":false},"reportOnly":{"upgrade-insecure-requests":false,"block-all-mixed-content":false}}, 10 | csrf_check_origin: true, 11 | track_server_fetches: false, 12 | embedded: false, 13 | env_public_prefix: 'PUBLIC_', 14 | env_private_prefix: '', 15 | hooks: null, // added lazily, via `get_hooks` 16 | preload_strategy: "modulepreload", 17 | root, 18 | service_worker: false, 19 | templates: { 20 | app: ({ head, body, assets, nonce, env }) => "\n\n \n \n \n \n \n \n\n Svupa\n " + head + "\n \n \n
" + body + "
\n \n \n\n\n\n", 21 | error: ({ status, message }) => "\n\n\t\n\t\t\n\t\t" + message + "\n\n\t\t\n\t\n\t\n\t\t
\n\t\t\t" + status + "\n\t\t\t
\n\t\t\t\t

" + message + "

\n\t\t\t
\n\t\t
\n\t\n\n" 22 | }, 23 | version_hash: "1lcuvn8" 24 | }; 25 | 26 | export function get_hooks() { 27 | return {}; 28 | } 29 | 30 | export { set_assets, set_building, set_private_env, set_public_env }; 31 | -------------------------------------------------------------------------------- /.svelte-kit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": {}, 4 | "rootDirs": [ 5 | "..", 6 | "./types" 7 | ], 8 | "importsNotUsedAsValues": "error", 9 | "isolatedModules": true, 10 | "preserveValueImports": true, 11 | "lib": [ 12 | "esnext", 13 | "DOM", 14 | "DOM.Iterable" 15 | ], 16 | "moduleResolution": "node", 17 | "module": "esnext", 18 | "noEmit": true, 19 | "target": "esnext" 20 | }, 21 | "include": [ 22 | "ambient.d.ts", 23 | "./types/**/$types.d.ts", 24 | "../vite.config.js", 25 | "../vite.config.ts", 26 | "../src/**/*.js", 27 | "../src/**/*.ts", 28 | "../src/**/*.svelte", 29 | "../tests/**/*.js", 30 | "../tests/**/*.ts", 31 | "../tests/**/*.svelte" 32 | ], 33 | "exclude": [ 34 | "../node_modules/**", 35 | "./[!ambient.d.ts]**", 36 | "../src/service-worker.js", 37 | "../src/service-worker.ts", 38 | "../src/service-worker.d.ts" 39 | ] 40 | } -------------------------------------------------------------------------------- /.svelte-kit/types/route_meta_data.json: -------------------------------------------------------------------------------- 1 | { 2 | "/(main)": [ 3 | "src/routes/(main)/+page.js" 4 | ], 5 | "/": [], 6 | "/(main)/contact": [ 7 | "src/routes/(main)/contact/+page.js" 8 | ], 9 | "/(iframe)/demo": [], 10 | "/(iframe)/demo/[row_id]": [ 11 | "src/routes/(iframe)/demo/[row_id]/+page.js" 12 | ], 13 | "/(iframe)/demo/[row_id]/[mode]": [ 14 | "src/routes/(iframe)/demo/[row_id]/[mode]/+page.js" 15 | ], 16 | "/(main)/depth": [ 17 | "src/routes/(main)/depth/+page.js" 18 | ] 19 | } -------------------------------------------------------------------------------- /.svelte-kit/types/src/routes/$types.d.ts: -------------------------------------------------------------------------------- 1 | import type * as Kit from '@sveltejs/kit'; 2 | 3 | type Expand = T extends infer O ? { [K in keyof O]: O[K] } : never; 4 | // @ts-ignore 5 | type MatcherParam = M extends (param : string) => param is infer U ? U extends string ? U : string : string; 6 | type RouteParams = { }; 7 | type RouteId = '/'; 8 | type MaybeWithVoid = {} extends T ? T | void : T; 9 | export type RequiredKeys = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T]; 10 | type OutputDataShape = MaybeWithVoid> & Partial> & Record> 11 | type EnsureDefined = T extends null | undefined ? {} : T; 12 | type OptionalUnion, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude]?: never } & U : never; 13 | export type Snapshot = Kit.Snapshot; 14 | type LayoutRouteId = RouteId | "/(iframe)/demo/[row_id]" | "/(iframe)/demo/[row_id]/[mode]" | "/(main)" | "/(main)/contact" | "/(main)/depth" | null 15 | type LayoutParams = RouteParams & { row_id?: string; mode?: string } 16 | type LayoutParentData = EnsureDefined<{}>; 17 | 18 | export type LayoutServerData = null; 19 | export type LayoutData = Expand; -------------------------------------------------------------------------------- /.svelte-kit/types/src/routes/(iframe)/demo/$types.d.ts: -------------------------------------------------------------------------------- 1 | import type * as Kit from '@sveltejs/kit'; 2 | 3 | type Expand = T extends infer O ? { [K in keyof O]: O[K] } : never; 4 | // @ts-ignore 5 | type MatcherParam = M extends (param : string) => param is infer U ? U extends string ? U : string : string; 6 | type RouteParams = { }; 7 | type RouteId = '/(iframe)/demo'; 8 | type MaybeWithVoid = {} extends T ? T | void : T; 9 | export type RequiredKeys = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T]; 10 | type OutputDataShape = MaybeWithVoid> & Partial> & Record> 11 | type EnsureDefined = T extends null | undefined ? {} : T; 12 | type OptionalUnion, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude]?: never } & U : never; 13 | export type Snapshot = Kit.Snapshot; 14 | type LayoutRouteId = RouteId | "/(iframe)/demo/[row_id]" | "/(iframe)/demo/[row_id]/[mode]" 15 | type LayoutParams = RouteParams & { row_id?: string; mode?: string } 16 | type LayoutParentData = EnsureDefined; 17 | 18 | export type LayoutServerData = null; 19 | export type LayoutData = Expand; -------------------------------------------------------------------------------- /.svelte-kit/types/src/routes/(iframe)/demo/[row_id]/$types.d.ts: -------------------------------------------------------------------------------- 1 | import type * as Kit from '@sveltejs/kit'; 2 | 3 | type Expand = T extends infer O ? { [K in keyof O]: O[K] } : never; 4 | // @ts-ignore 5 | type MatcherParam = M extends (param : string) => param is infer U ? U extends string ? U : string : string; 6 | type RouteParams = { row_id: string }; 7 | type RouteId = '/(iframe)/demo/[row_id]'; 8 | type MaybeWithVoid = {} extends T ? T | void : T; 9 | export type RequiredKeys = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T]; 10 | type OutputDataShape = MaybeWithVoid> & Partial> & Record> 11 | type EnsureDefined = T extends null | undefined ? {} : T; 12 | type OptionalUnion, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude]?: never } & U : never; 13 | export type Snapshot = Kit.Snapshot; 14 | type PageParentData = Omit, keyof import('../$types.js').LayoutData> & EnsureDefined; 15 | 16 | export type EntryGenerator = () => Promise> | Array; 17 | export type PageServerData = null; 18 | export type PageLoad = OutputDataShape> = Kit.Load; 19 | export type PageLoadEvent = Parameters[0]; 20 | export type PageData = Expand>>> & OptionalUnion>>>>>; -------------------------------------------------------------------------------- /.svelte-kit/types/src/routes/(iframe)/demo/[row_id]/[mode]/$types.d.ts: -------------------------------------------------------------------------------- 1 | import type * as Kit from '@sveltejs/kit'; 2 | 3 | type Expand = T extends infer O ? { [K in keyof O]: O[K] } : never; 4 | // @ts-ignore 5 | type MatcherParam = M extends (param : string) => param is infer U ? U extends string ? U : string : string; 6 | type RouteParams = { row_id: string; mode: string }; 7 | type RouteId = '/(iframe)/demo/[row_id]/[mode]'; 8 | type MaybeWithVoid = {} extends T ? T | void : T; 9 | export type RequiredKeys = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T]; 10 | type OutputDataShape = MaybeWithVoid> & Partial> & Record> 11 | type EnsureDefined = T extends null | undefined ? {} : T; 12 | type OptionalUnion, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude]?: never } & U : never; 13 | export type Snapshot = Kit.Snapshot; 14 | type PageParentData = Omit, keyof import('../../$types.js').LayoutData> & EnsureDefined; 15 | 16 | export type EntryGenerator = () => Promise> | Array; 17 | export type PageServerData = null; 18 | export type PageLoad = OutputDataShape> = Kit.Load; 19 | export type PageLoadEvent = Parameters[0]; 20 | export type PageData = Expand>>> & OptionalUnion>>>>>; -------------------------------------------------------------------------------- /.svelte-kit/types/src/routes/(main)/$types.d.ts: -------------------------------------------------------------------------------- 1 | import type * as Kit from '@sveltejs/kit'; 2 | 3 | type Expand = T extends infer O ? { [K in keyof O]: O[K] } : never; 4 | // @ts-ignore 5 | type MatcherParam = M extends (param : string) => param is infer U ? U extends string ? U : string : string; 6 | type RouteParams = { }; 7 | type RouteId = '/(main)'; 8 | type MaybeWithVoid = {} extends T ? T | void : T; 9 | export type RequiredKeys = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T]; 10 | type OutputDataShape = MaybeWithVoid> & Partial> & Record> 11 | type EnsureDefined = T extends null | undefined ? {} : T; 12 | type OptionalUnion, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude]?: never } & U : never; 13 | export type Snapshot = Kit.Snapshot; 14 | type PageParentData = Omit, keyof LayoutData> & EnsureDefined; 15 | type LayoutRouteId = RouteId | "/(main)" | "/(main)/contact" | "/(main)/depth" 16 | type LayoutParams = RouteParams & { } 17 | type LayoutParentData = EnsureDefined; 18 | 19 | export type PageServerData = null; 20 | export type PageLoad = OutputDataShape> = Kit.Load; 21 | export type PageLoadEvent = Parameters[0]; 22 | export type PageData = Expand> & OptionalUnion>>>; 23 | export type LayoutServerData = null; 24 | export type LayoutData = Expand; -------------------------------------------------------------------------------- /.svelte-kit/types/src/routes/(main)/contact/$types.d.ts: -------------------------------------------------------------------------------- 1 | import type * as Kit from '@sveltejs/kit'; 2 | 3 | type Expand = T extends infer O ? { [K in keyof O]: O[K] } : never; 4 | // @ts-ignore 5 | type MatcherParam = M extends (param : string) => param is infer U ? U extends string ? U : string : string; 6 | type RouteParams = { }; 7 | type RouteId = '/(main)/contact'; 8 | type MaybeWithVoid = {} extends T ? T | void : T; 9 | export type RequiredKeys = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T]; 10 | type OutputDataShape = MaybeWithVoid> & Partial> & Record> 11 | type EnsureDefined = T extends null | undefined ? {} : T; 12 | type OptionalUnion, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude]?: never } & U : never; 13 | export type Snapshot = Kit.Snapshot; 14 | type PageParentData = Omit, keyof import('../$types.js').LayoutData> & EnsureDefined; 15 | 16 | export type PageServerData = null; 17 | export type PageLoad = OutputDataShape> = Kit.Load; 18 | export type PageLoadEvent = Parameters[0]; 19 | export type PageData = Expand> & OptionalUnion>>>; -------------------------------------------------------------------------------- /.svelte-kit/types/src/routes/(main)/depth/$types.d.ts: -------------------------------------------------------------------------------- 1 | import type * as Kit from '@sveltejs/kit'; 2 | 3 | type Expand = T extends infer O ? { [K in keyof O]: O[K] } : never; 4 | // @ts-ignore 5 | type MatcherParam = M extends (param : string) => param is infer U ? U extends string ? U : string : string; 6 | type RouteParams = { }; 7 | type RouteId = '/(main)/depth'; 8 | type MaybeWithVoid = {} extends T ? T | void : T; 9 | export type RequiredKeys = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T]; 10 | type OutputDataShape = MaybeWithVoid> & Partial> & Record> 11 | type EnsureDefined = T extends null | undefined ? {} : T; 12 | type OptionalUnion, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude]?: never } & U : never; 13 | export type Snapshot = Kit.Snapshot; 14 | type PageParentData = Omit, keyof import('../$types.js').LayoutData> & EnsureDefined; 15 | 16 | export type PageServerData = null; 17 | export type PageLoad = OutputDataShape> = Kit.Load; 18 | export type PageLoadEvent = Parameters[0]; 19 | export type PageData = Expand> & OptionalUnion>>>; -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:21-alpine3.18 2 | 3 | 4 | WORKDIR /app 5 | RUN echo "fs.inotify.max_user_watches=524288" >> /etc/sysctl.conf 6 | 7 | COPY ./svelte.config.js ./ 8 | COPY ./tailwind.config.cjs ./ 9 | COPY ./vite.config.js ./ 10 | COPY ./package*.json ./ 11 | 12 | RUN npm install -g npm@10.2.5 13 | RUN npm install 14 | RUN npm update 15 | 16 | USER root 17 | COPY ./ ./ 18 | 19 | EXPOSE 3000 20 | 21 | ENV HOST=0.0.0.0 22 | 23 | CMD npm run dev -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Julian Freyberg 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 | 4 |

5 |
6 | 7 | Svupa is a [Svelte](https://svelte.dev/) + [Supabase](https://supabase.com/) framework to create realtime apps with a single source of truth and optimistic updates. It leverages the power of Supabase to create Svelte stores that are synchronized across clients. 8 | 9 | 10 | Here's how it works
11 | 12 | Supabase is an open-source database built on top of PostgreSQL. Svupa synchronizes (part of) the data from this database directly into a Svelte store. Since multiple users can subscribe to the same data, all clients will see the same state. With Svupa, it's as easy to react to changes in your database as it is to react to changes in a Svelte store - and that's damn easy. 13 | 14 | 15 | If you're still confused, don't worry! You can see an example right here: 16 | 17 |

18 | And a conceptual overview here: 19 |

20 |
21 | 22 | I also provide much more information on the project website, including a live demo. 23 | 24 | Why Supabase?
25 | 26 | Supabase and Svelte are an excellent fit because they are built on similar paradigms, are rapidly gaining popularity, and focus on developer experience. Svupa seamlessly combines the two to create a framework that enables developers to build realtime applications much faster and without headaches. 27 | 28 | Project Status and Submission to SvelteHack
29 | 30 | Currently, Svupa is in a proof-of-concept state. My submission is not a fully functional framework but a demo of what Svupa can achieve, which is presented on the demo website I am submitting. I plan to release Svupa as a framework in the future, but that is way beyond the scope of the two months intended for this hackathon, even if I were working full-time on this. Maybe this hackaton helps me find some contributors. Of course, I will not touch the repository after the deadline until the winners are announced, or switch to another repo if that is prefered by Svelte Society. Considering that there are multi-million-dollar-backed startups developing solutions that achieve things similar to Svupa, e.g., Convex, I consider my submission a worthy contribution to the open-source community and a proper example of how Svelte can be integrated with other web technologies to create cool things. 31 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | webapp: 5 | build: 6 | context: . 7 | container_name: svupa-webapp 8 | volumes: 9 | - type: bind 10 | source: ./src 11 | target: /app/src 12 | - type: bind 13 | source: ./static 14 | target: /app/static 15 | ports: 16 | - 127.0.0.1:3000:3000 -------------------------------------------------------------------------------- /header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jfreyberg/svupa/d501e5f470cf26bb1a3d0847adaad2f32f5b1d54/header.png -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "$lib": ["src/api"], 7 | "$lib/*": ["src/api/*"] 8 | } 9 | }, 10 | "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte", "src/utils/svupa.ts"] 11 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svupa", 3 | "version": "0.0.1", 4 | "type": "module", 5 | "license": "MIT", 6 | "files": [ 7 | "dist" 8 | ], 9 | "scripts": { 10 | "build:tailwind": "npx postcss static/tail.css -o static/styles.css", 11 | "dev": "vite dev --host", 12 | "prod": "npm run build && ls && ls ./build && node build/index.js", 13 | "build": "vite build", 14 | "package": "svelte-kit package", 15 | "preview": "svelte-kit preview" 16 | }, 17 | "dependencies": { 18 | "@sveltejs/package": "^2.2.4" 19 | }, 20 | "devDependencies": { 21 | "@supabase/supabase-js": "2.39.1", 22 | "@sveltejs/adapter-vercel": "next", 23 | "@sveltejs/kit": ">=1.15.2", 24 | "svelte": "3.58.0", 25 | "svelte-highlight": "7.2.0", 26 | "svelte-preprocess": "^4.10.4", 27 | "tailwindcss": "^3.2.7", 28 | "uuid": "9.0.0", 29 | "vite": "^4.0.0", 30 | "typescript": "^4.3.3" 31 | }, 32 | "exports": { 33 | ".": { 34 | "types": "./dist/index.d.ts", 35 | "svelte": "./dist/index.js" 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Svupa 11 | %sveltekit.head% 12 | 13 | 14 |
%sveltekit.body%
15 | 16 | 21 | 22 | 23 | 27 | -------------------------------------------------------------------------------- /src/components/Button.svelte: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/CodeSnippet.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | {@html atomOneLight} 19 | 20 |
23 |
24 | {#if filename} 25 |
{filename}
26 | {/if} 27 | 28 |
29 |
32 |
{}} 34 | on:click={() => { 35 | copied = true; 36 | navigator.clipboard.writeText(code); 37 | setTimeout(() => { 38 | copied = false; 39 | }, 1000); 40 | }} 41 | class="{copied 42 | ? 'bg-gray-200 hover:bg-gray-300' 43 | : 'bg-white hover:bg-gray-100'} group-hover/InputField:shadow-md group-hover/InputField:border-gray-300 p-1 border-2 cursor-pointer shadow-sm hover:shadow-md border-gray-200 dark:border-[rgba(255,255,255,0.1)] rounded-md items-center transition-all duration-200" 44 | > 45 | copy code 46 |
47 |
48 |
49 | -------------------------------------------------------------------------------- /src/components/CopyInput.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 |
18 |
21 | {text} 22 |
23 |
26 |
29 | 30 | {#if text && text.length > 0} 31 | 36 | {/if} 37 |
38 |
{}} 40 | on:click={() => { 41 | copied = true; 42 | navigator.clipboard.writeText(text); 43 | setTimeout(() => { 44 | copied = false; 45 | }, 500); 46 | }} 47 | class="{copied 48 | ? 'bg-gray-200 hover:bg-gray-300' 49 | : 'bg-white hover:bg-gray-100'} group-hover/InputField:shadow-md group-hover/InputField:border-gray-300 p-1 border-2 cursor-pointer shadow-sm hover:shadow-md border-gray-200 rounded-md items-center transition-all duration-200" 50 | > 51 | copy 52 |
53 |
{}} 55 | on:click={() => { 56 | if (newWindow) { 57 | openInNewWindow(text); 58 | } else { 59 | openInNewTab(text); 60 | } 61 | }} 62 | class="group-hover/InputField:shadow-md group-hover/InputField:border-gray-300 p-1 border-2 cursor-pointer shadow-sm hover:shadow-md border-gray-200 dark:border-[rgba(255,255,255,0.1)] bg-white rounded-md items-center hover:bg-gray-100 transition-all duration-200" 63 | > 64 | 65 |
66 |
67 |
68 | -------------------------------------------------------------------------------- /src/components/DataBall.svelte: -------------------------------------------------------------------------------- 1 | 69 | 70 |
71 | {#if $element} 72 | 73 |
{ 79 | scale = 90; 80 | await upsertRow(); 81 | if (!optimistic) { 82 | setTimeout(() => { 83 | scale = 100; 84 | }, 100); 85 | } else { 86 | scale = 100; 87 | } 88 | }} 89 | > 90 | {$element.number} 91 |
92 | {/if} 93 |
94 | -------------------------------------------------------------------------------- /src/components/DataTable.svelte: -------------------------------------------------------------------------------- 1 | 104 | 105 | {#if $header} 106 |
107 | {#each $rows as row, i (row.id)} 108 | {#if i == 0} 109 |
110 | {#each $header as key (key)} 111 | {#if key != "id"} 112 |
116 | {key} 117 |
118 | {/if} 119 | {/each} 120 |
124 | Options 125 |
126 |
127 | {/if} 128 | 129 |
130 | {#each $header as key (key)} 131 | {#if key != "id"} 132 |
136 | {row[key]} 137 |
138 | {/if} 139 | {/each} 140 |
144 | 145 | 146 | { 150 | deleteRow(row.id); 151 | }} 152 | /> 153 | 154 | 155 | { 159 | upsertRow(row.id); 160 | }} 161 | /> 162 |
163 |
164 | {/each} 165 |
166 | {#if $size < 5} 167 |
170 |
184 | {/if} 185 | {/if} 186 | -------------------------------------------------------------------------------- /src/components/DemoBox.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 |
10 | 15 | 21 | 28 | 29 | 34 |
35 |
38 | 39 | 40 |
41 |
42 |
43 | 71 |
72 | Above you see two iframes connecting to the same data source using Svupa. 73 | The first one uses pessimistic updates, the second one uses optimistic updates. 74 |
-------------------------------------------------------------------------------- /src/components/FeatureCard.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
9 | 10 |
{title}
11 |

12 |
13 | -------------------------------------------------------------------------------- /src/components/FeatureItem.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 | {text} 7 |
8 | -------------------------------------------------------------------------------- /src/components/Frame.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | {#if url} 8 |
11 |
12 |
13 |
14 |
{url}
15 |
16 |